47
48
// ------------------------------------------------------------------------
49
// string to be echo'ed when running command in shell, used to indicate that the command has finished running
50
/** Sub-string to be echo'ed when running command in shell, used to indicate that the command has finished running */
50
51
public final static String DONE_MARKUP_STRING = "--RSE:donedonedone:--"; //$NON-NLS-1$
52
//command delimiter for shell
53
/** Sub-string to be echoed when running a command in shell. */
54
public final static String BEGIN_END_TAG = "BEGIN-END-TAG:"; //$NON-NLS-1$
56
/** Command delimiter for shell */
53
57
public final static String CMD_DELIMITER = "\n"; //$NON-NLS-1$
59
/** Shell "echo" command */
55
60
public final static String SHELL_ECHO_CMD = " echo "; //$NON-NLS-1$
57
private final static int DEFAULT_TIMEOUT_VALUE = 15000; // in milliseconds
62
/** Default command separator */
63
public final static char CMD_SEPARATOR = ';';
65
/** Default timeout value used for executing commands, in milliseconds */
66
private final static int DEFAULT_TIMEOUT_VALUE = 15000;
59
68
// ------------------------------------------------------------------------
61
70
// ------------------------------------------------------------------------
62
71
private IRemoteSystemProxy fProxy = null;
63
72
private IHostShell fHostShell = null;
64
private BufferedReader fBufferReader = null;
65
private ExecutorService fExecutor = Executors.newFixedThreadPool(1);
73
private BufferedReader fInputBufferReader = null;
74
private BufferedReader fErrorBufferReader = null;
75
private final ExecutorService fExecutor = Executors.newFixedThreadPool(1);
66
76
private boolean fIsConnected = false;
77
private final Random fRandom = new Random(System.currentTimeMillis());
78
private int fReturnValue;
68
80
// ------------------------------------------------------------------------
70
82
// ------------------------------------------------------------------------
85
* Create a new command shell
88
* The RSE proxy for this shell
71
90
public CommandShell(IRemoteSystemProxy proxy) {
131
149
public CommandResult call() throws IOException, CancellationException {
132
150
final ArrayList<String> result = new ArrayList<String>();
135
152
synchronized (fHostShell) {
136
fHostShell.writeToShell(formatShellCommand(command));
153
// Initialize return value which will be updated in isAliasEchoResult()
156
int startAlias = fRandom.nextInt();
157
int endAlias = fRandom.nextInt();
158
fHostShell.writeToShell(formatShellCommand(command, startAlias, endAlias));
138
while ((nextLine = fBufferReader.readLine()) != null) {
161
boolean isStartFound = false;
162
while ((nextLine = fInputBufferReader.readLine()) != null) {
140
164
if (monitor.isCanceled()) {
142
throw new CancellationException();
145
if (nextLine.contains(DONE_MARKUP_STRING) && nextLine.contains(SHELL_ECHO_CMD)) {
166
throw new CancellationException();
169
// check if line contains echoed start alias
170
if (isAliasEchoResult(nextLine, startAlias, true)) {
175
// check if line contains is the end mark-up. This will retrieve also
176
// the return value of the actual command.
177
if (isAliasEchoResult(nextLine, endAlias, false)) {
182
// 1) start hasn't been found or
183
// 2) line is an echo of the command or
184
// 3) line is an echo of the end mark-up
186
isCommandEcho(nextLine, command) ||
187
nextLine.contains(getEchoResult(endAlias)))
192
// Now it's time add to the result
193
result.add(nextLine);
150
while ((nextLine = fBufferReader.readLine()) != null) {
151
// check if job was cancelled
152
if (monitor.isCanceled()) {
154
throw new CancellationException();
196
// Read any left over output
157
if (!nextLine.contains(DONE_MARKUP_STRING)) {
158
result.add(nextLine);
160
if (checkReturnValue) {
161
returnValue = Integer.valueOf(nextLine.substring(DONE_MARKUP_STRING.length()+1));
199
// Read error stream output when command failed.
200
if (fReturnValue != 0) {
201
while(fErrorBufferReader.ready()) {
202
if ((nextLine = fErrorBufferReader.readLine()) != null) {
203
result.add(nextLine);
169
return new CommandResult(returnValue, result.toArray(new String[result.size()]));
208
return new CommandResult(fReturnValue, result.toArray(new String[result.size()]));
185
224
throw new ExecutionException(Messages.TraceControl_ShellNotConnected, null);
188
227
// ------------------------------------------------------------------------
189
228
// Helper methods
190
229
// ------------------------------------------------------------------------
192
* Flushes the buffer reader
231
* Flushes the buffer reader
193
232
* @throws IOException
195
234
private void flushInput() throws IOException {
196
235
char[] cbuf = new char[1];
197
while (fBufferReader.ready()) {
198
if (fBufferReader.read(cbuf, 0, 1) == -1) {
236
while (fInputBufferReader.ready()) {
237
if (fInputBufferReader.read(cbuf, 0, 1) == -1) {
205
* format the command to be sent into the shell command with the done markup string.
206
* The done markup string is needed so we can tell that end of output has been reached.
244
* Format the command to be sent into the shell command with start and end marker strings.
245
* The start marker is need to know when the actual command output starts. The end marker
246
* string is needed so we can tell that end of output has been reached.
248
* @param cmd The actual command
249
* @param startAlias The command alias for start marker
250
* @param endAlias The command alias for end marker
209
251
* @return formatted command string
211
private String formatShellCommand(String cmd) {
212
if (cmd == null || cmd.equals("")) //$NON-NLS-1$
253
private static String formatShellCommand(String cmd, int startAlias, int endAlias) {
254
if (cmd == null || cmd.equals("")) { //$NON-NLS-1$
214
257
StringBuffer formattedCommand = new StringBuffer();
215
// Make a multi line command by using \ and \r. This is needed for matching
216
// the DONE_MARKUP_STRING in echoed command when having a long command
217
// (bigger than max SSH line)
218
formattedCommand.append(cmd).append("\\\r;"); //$NON-NLS-1$
219
formattedCommand.append(SHELL_ECHO_CMD).append(DONE_MARKUP_STRING);
220
formattedCommand.append(" $?"); //$NON-NLS-1$
258
// Make multi-line command.
259
// Wrap actual command with start marker and end marker to wrap actual command.
260
formattedCommand.append(getEchoCmd(startAlias));
261
formattedCommand.append(CMD_DELIMITER);
262
formattedCommand.append(cmd);
263
formattedCommand.append(CMD_DELIMITER);
264
formattedCommand.append(getEchoCmd(endAlias));
221
265
formattedCommand.append(CMD_DELIMITER);
222
266
return formattedCommand.toString();
270
* Creates a echo command line in the format: echo <start tag> <alias> <end tag> $?
272
* @param alias The command alias integer to be included in the echoed message.
273
* @return the echo command line
275
private static String getEchoCmd(int alias) {
276
return SHELL_ECHO_CMD + getEchoResult(alias) + "$?"; //$NON-NLS-1$
280
* Creates the expected result for a given command alias:
281
* <start tag> <alias> <end tag> $?
283
* @param alias The command alias integer to be included in the echoed message.
284
* @return the expected echo result
286
private static String getEchoResult(int alias) {
287
return BEGIN_END_TAG + String.valueOf(alias) + DONE_MARKUP_STRING;
291
* Verifies if given command line contains a command alias echo result.
293
* @param line The output line to test.
294
* @param alias The command alias
295
* @param checkReturnValue <code>true</code> to retrieve command result (previous command) <code>false</code>
296
* @return <code>true</code> if output line is a command alias echo result else <code>false</code>
298
private boolean isAliasEchoResult(String line, int alias, boolean checkReturnValue) {
299
String expected = getEchoResult(alias);
300
if (line.startsWith(expected)) {
301
if (!checkReturnValue) {
303
int k = Integer.valueOf(line.substring(expected.length()));
305
} catch (NumberFormatException e) {
311
int index = line.indexOf(expected);
313
if (line.indexOf(SHELL_ECHO_CMD) == -1) {
322
* Verifies if output line is an echo of the given command line. If the
323
* output line is longer then the maximum line lengths (e.g. for ssh), the
324
* shell adds a line break character. This method takes this in
325
* consideration by comparing the command line without any whitespaces.
328
* The output line to verify
330
* The command executed
331
* @return <code>true</code> if it's an echoed command line else
334
@SuppressWarnings("nls")
335
private static boolean isCommandEcho(String line, String cmd) {
336
String s1 = line.replaceAll("\\s","");
337
String s2 = cmd.replaceAll("\\s","");
338
s2 = s2.replaceAll("(\\*)", "(\\\\*)");
339
String patternStr = ".*(" + s2 +")$";
340
return s1.matches(patternStr);