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