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.

JschSession.java 10KB

Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
Client-side protocol V2 support for fetching Make all transports request protocol V2 when fetching. Depending on the transport, set the GIT_PROTOCOL environment variable (file and ssh), pass the Git-Protocol header (http), or set the hidden "\0version=2\0" (git anon). We'll fall back to V0 if the server doesn't reply with a version 2 answer. A user can control which protocol the client requests via the git config protocol.version; if not set, JGit requests protocol V2 for fetching. Pushing always uses protocol V0 still. In the API, there is only a new Transport.openFetch() version that takes a collection of RefSpecs plus additional patterns to construct the Ref prefixes for the "ls-refs" command in protocol V2. If none are given, the server will still advertise all refs, even in protocol V2. BasePackConnection.readAdvertisedRefs() handles falling back to protocol V0. It newly returns true if V0 was used and the advertised refs were read, and false if V2 is used and an explicit "ls-refs" is needed. (This can't be done transparently inside readAdvertisedRefs() because a "stateless RPC" transport like TransportHttp may need to open a new connection for writing.) BasePackFetchConnection implements the changes needed for the protocol V2 "fetch" command (stateless protocol, simplified ACK handling, delimiters, section headers). In TransportHttp, change readSmartHeaders() to also recognize the "version 2" packet line as a valid smart server indication. Adapt tests, and run all the HTTP tests not only with both HTTP connection factories (JDK and Apache HttpClient) but also with both protocol V0 and V2. The SSH tests are much slower and much more focused on the SSH protocol and SSH key handling. Factor out two very simple cloning and pulling tests and make those run with protocol V2. Bug: 553083 Change-Id: I357c7f5daa7efb2872f1c64ee6f6d54229031ae1 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * Copyright (C) 2009, Constantine Plotnikov <constantine.plotnikov@gmail.com>
  3. * Copyright (C) 2008-2009, Google Inc.
  4. * Copyright (C) 2009, Google, Inc.
  5. * Copyright (C) 2009, JetBrains s.r.o.
  6. * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
  7. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  8. *
  9. * This program and the accompanying materials are made available under the
  10. * terms of the Eclipse Distribution License v. 1.0 which is available at
  11. * https://www.eclipse.org/org/documents/edl-v10.php.
  12. *
  13. * SPDX-License-Identifier: BSD-3-Clause
  14. */
  15. //TODO(ms): move to org.eclipse.jgit.ssh.jsch in 6.0
  16. package org.eclipse.jgit.transport;
  17. import java.io.BufferedOutputStream;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.OutputStream;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.Collections;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.concurrent.Callable;
  27. import java.util.concurrent.TimeUnit;
  28. import org.eclipse.jgit.errors.TransportException;
  29. import org.eclipse.jgit.internal.transport.jsch.JSchText;
  30. import org.eclipse.jgit.util.io.IsolatedOutputStream;
  31. import com.jcraft.jsch.Channel;
  32. import com.jcraft.jsch.ChannelExec;
  33. import com.jcraft.jsch.ChannelSftp;
  34. import com.jcraft.jsch.JSchException;
  35. import com.jcraft.jsch.Session;
  36. import com.jcraft.jsch.SftpException;
  37. /**
  38. * Run remote commands using Jsch.
  39. * <p>
  40. * This class is the default session implementation using Jsch. Note that
  41. * {@link org.eclipse.jgit.transport.JschConfigSessionFactory} is used to create
  42. * the actual session passed to the constructor.
  43. */
  44. public class JschSession implements RemoteSession2 {
  45. final Session sock;
  46. final URIish uri;
  47. /**
  48. * Create a new session object by passing the real Jsch session and the URI
  49. * information.
  50. *
  51. * @param session
  52. * the real Jsch session created elsewhere.
  53. * @param uri
  54. * the URI information for the remote connection
  55. */
  56. public JschSession(Session session, URIish uri) {
  57. sock = session;
  58. this.uri = uri;
  59. }
  60. /** {@inheritDoc} */
  61. @Override
  62. public Process exec(String command, int timeout) throws IOException {
  63. return exec(command, Collections.emptyMap(), timeout);
  64. }
  65. /** {@inheritDoc} */
  66. @Override
  67. public Process exec(String command, Map<String, String> environment,
  68. int timeout) throws IOException {
  69. return new JschProcess(command, environment, timeout);
  70. }
  71. /** {@inheritDoc} */
  72. @Override
  73. public void disconnect() {
  74. if (sock.isConnected())
  75. sock.disconnect();
  76. }
  77. /**
  78. * A kludge to allow {@link org.eclipse.jgit.transport.TransportSftp} to get
  79. * an Sftp channel from Jsch. Ideally, this method would be generic, which
  80. * would require implementing generic Sftp channel operations in the
  81. * RemoteSession class.
  82. *
  83. * @return a channel suitable for Sftp operations.
  84. * @throws com.jcraft.jsch.JSchException
  85. * on problems getting the channel.
  86. * @deprecated since 5.2; use {@link #getFtpChannel()} instead
  87. */
  88. @Deprecated
  89. public Channel getSftpChannel() throws JSchException {
  90. return sock.openChannel("sftp"); //$NON-NLS-1$
  91. }
  92. /**
  93. * {@inheritDoc}
  94. *
  95. * @since 5.2
  96. */
  97. @Override
  98. public FtpChannel getFtpChannel() {
  99. return new JschFtpChannel();
  100. }
  101. /**
  102. * Implementation of Process for running a single command using Jsch.
  103. * <p>
  104. * Uses the Jsch session to do actual command execution and manage the
  105. * execution.
  106. */
  107. private class JschProcess extends Process {
  108. private ChannelExec channel;
  109. final int timeout;
  110. private InputStream inputStream;
  111. private OutputStream outputStream;
  112. private InputStream errStream;
  113. /**
  114. * Opens a channel on the session ("sock") for executing the given
  115. * command, opens streams, and starts command execution.
  116. *
  117. * @param commandName
  118. * the command to execute
  119. * @param environment
  120. * environment variables to pass on
  121. * @param tms
  122. * the timeout value, in seconds, for the command.
  123. * @throws TransportException
  124. * on problems opening a channel or connecting to the remote
  125. * host
  126. * @throws IOException
  127. * on problems opening streams
  128. */
  129. JschProcess(String commandName, Map<String, String> environment,
  130. int tms) throws TransportException, IOException {
  131. timeout = tms;
  132. try {
  133. channel = (ChannelExec) sock.openChannel("exec"); //$NON-NLS-1$
  134. if (environment != null) {
  135. for (Map.Entry<String, String> envVar : environment
  136. .entrySet()) {
  137. channel.setEnv(envVar.getKey(), envVar.getValue());
  138. }
  139. }
  140. channel.setCommand(commandName);
  141. setupStreams();
  142. channel.connect(timeout > 0 ? timeout * 1000 : 0);
  143. if (!channel.isConnected()) {
  144. closeOutputStream();
  145. throw new TransportException(uri,
  146. JSchText.get().connectionFailed);
  147. }
  148. } catch (JSchException e) {
  149. closeOutputStream();
  150. throw new TransportException(uri, e.getMessage(), e);
  151. }
  152. }
  153. private void closeOutputStream() {
  154. if (outputStream != null) {
  155. try {
  156. outputStream.close();
  157. } catch (IOException ioe) {
  158. // ignore
  159. }
  160. }
  161. }
  162. private void setupStreams() throws IOException {
  163. inputStream = channel.getInputStream();
  164. // JSch won't let us interrupt writes when we use our InterruptTimer
  165. // to break out of a long-running write operation. To work around
  166. // that we spawn a background thread to shuttle data through a pipe,
  167. // as we can issue an interrupted write out of that. Its slower, so
  168. // we only use this route if there is a timeout.
  169. OutputStream out = channel.getOutputStream();
  170. if (timeout <= 0) {
  171. outputStream = out;
  172. } else {
  173. IsolatedOutputStream i = new IsolatedOutputStream(out);
  174. outputStream = new BufferedOutputStream(i, 16 * 1024);
  175. }
  176. errStream = channel.getErrStream();
  177. }
  178. @Override
  179. public InputStream getInputStream() {
  180. return inputStream;
  181. }
  182. @Override
  183. public OutputStream getOutputStream() {
  184. return outputStream;
  185. }
  186. @Override
  187. public InputStream getErrorStream() {
  188. return errStream;
  189. }
  190. @Override
  191. public int exitValue() {
  192. if (isRunning())
  193. throw new IllegalThreadStateException();
  194. return channel.getExitStatus();
  195. }
  196. private boolean isRunning() {
  197. return channel.getExitStatus() < 0 && channel.isConnected();
  198. }
  199. @Override
  200. public void destroy() {
  201. if (channel.isConnected())
  202. channel.disconnect();
  203. closeOutputStream();
  204. }
  205. @Override
  206. public int waitFor() throws InterruptedException {
  207. while (isRunning())
  208. Thread.sleep(100);
  209. return exitValue();
  210. }
  211. }
  212. private class JschFtpChannel implements FtpChannel {
  213. private ChannelSftp ftp;
  214. @Override
  215. public void connect(int timeout, TimeUnit unit) throws IOException {
  216. try {
  217. ftp = (ChannelSftp) sock.openChannel("sftp"); //$NON-NLS-1$
  218. ftp.connect((int) unit.toMillis(timeout));
  219. } catch (JSchException e) {
  220. ftp = null;
  221. throw new IOException(e.getLocalizedMessage(), e);
  222. }
  223. }
  224. @Override
  225. public void disconnect() {
  226. ftp.disconnect();
  227. ftp = null;
  228. }
  229. private <T> T map(Callable<T> op) throws IOException {
  230. try {
  231. return op.call();
  232. } catch (Exception e) {
  233. if (e instanceof SftpException) {
  234. throw new FtpChannel.FtpException(e.getLocalizedMessage(),
  235. ((SftpException) e).id, e);
  236. }
  237. throw new IOException(e.getLocalizedMessage(), e);
  238. }
  239. }
  240. @Override
  241. public boolean isConnected() {
  242. return ftp != null && sock.isConnected();
  243. }
  244. @Override
  245. public void cd(String path) throws IOException {
  246. map(() -> {
  247. ftp.cd(path);
  248. return null;
  249. });
  250. }
  251. @Override
  252. public String pwd() throws IOException {
  253. return map(() -> ftp.pwd());
  254. }
  255. @Override
  256. public Collection<DirEntry> ls(String path) throws IOException {
  257. return map(() -> {
  258. List<DirEntry> result = new ArrayList<>();
  259. for (Object e : ftp.ls(path)) {
  260. ChannelSftp.LsEntry entry = (ChannelSftp.LsEntry) e;
  261. result.add(new DirEntry() {
  262. @Override
  263. public String getFilename() {
  264. return entry.getFilename();
  265. }
  266. @Override
  267. public long getModifiedTime() {
  268. return entry.getAttrs().getMTime();
  269. }
  270. @Override
  271. public boolean isDirectory() {
  272. return entry.getAttrs().isDir();
  273. }
  274. });
  275. }
  276. return result;
  277. });
  278. }
  279. @Override
  280. public void rmdir(String path) throws IOException {
  281. map(() -> {
  282. ftp.rm(path);
  283. return null;
  284. });
  285. }
  286. @Override
  287. public void mkdir(String path) throws IOException {
  288. map(() -> {
  289. ftp.mkdir(path);
  290. return null;
  291. });
  292. }
  293. @Override
  294. public InputStream get(String path) throws IOException {
  295. return map(() -> ftp.get(path));
  296. }
  297. @Override
  298. public OutputStream put(String path) throws IOException {
  299. return map(() -> ftp.put(path));
  300. }
  301. @Override
  302. public void rm(String path) throws IOException {
  303. map(() -> {
  304. ftp.rm(path);
  305. return null;
  306. });
  307. }
  308. @Override
  309. public void rename(String from, String to) throws IOException {
  310. map(() -> {
  311. // Plain FTP rename will fail if "to" exists. Jsch knows about
  312. // the FTP extension "posix-rename@openssh.com", which will
  313. // remove "to" first if it exists.
  314. if (hasPosixRename()) {
  315. ftp.rename(from, to);
  316. } else if (!to.equals(from)) {
  317. // Try to remove "to" first. With git, we typically get this
  318. // when a lock file is moved over the file locked. Note that
  319. // the check for to being equal to from may still fail in
  320. // the general case, but for use with JGit's TransportSftp
  321. // it should be good enough.
  322. delete(to);
  323. ftp.rename(from, to);
  324. }
  325. return null;
  326. });
  327. }
  328. /**
  329. * Determine whether the server has the posix-rename extension.
  330. *
  331. * @return {@code true} if it is supported, {@code false} otherwise
  332. * @see <a href=
  333. * "https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD">OpenSSH
  334. * deviations and extensions to the published SSH protocol</a>
  335. * @see <a href=
  336. * "http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html">stdio.h:
  337. * rename()</a>
  338. */
  339. private boolean hasPosixRename() {
  340. return "1".equals(ftp.getExtension("posix-rename@openssh.com")); //$NON-NLS-1$//$NON-NLS-2$
  341. }
  342. }
  343. }