~ubuntu-branches/ubuntu/trusty/eclipse-linuxtools/trusty

« back to all changes in this revision

Viewing changes to lttng/org.eclipse.linuxtools.lttng2.ui/src/org/eclipse/linuxtools/internal/lttng2/ui/views/control/remote/CommandShell.java

  • Committer: Package Import Robot
  • Author(s): tony mancill
  • Date: 2013-05-13 21:43:22 UTC
  • mfrom: (1.2.1) (2.1.2 experimental)
  • Revision ID: package-import@ubuntu.com-20130513214322-6frgd9du1n0w2uo7
Tags: 1.2.1-1
* Team upload.
* New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/**********************************************************************
2
2
 * Copyright (c) 2012 Ericsson
3
 
 * 
 
3
 *
4
4
 * All rights reserved. This program and the accompanying materials are
5
5
 * made available under the terms of the Eclipse Public License v1.0 which
6
6
 * accompanies this distribution, and is available at
7
7
 * http://www.eclipse.org/legal/epl-v10.html
8
 
 * 
9
 
 * Contributors: 
 
8
 *
 
9
 * Contributors:
10
10
 *   Patrick Tasse - Initial API and implementation
11
11
 *   Bernd Hufmann - Updated using Executor Framework
12
12
 **********************************************************************/
16
16
import java.io.IOException;
17
17
import java.io.InputStreamReader;
18
18
import java.util.ArrayList;
 
19
import java.util.Random;
19
20
import java.util.concurrent.Callable;
20
21
import java.util.concurrent.CancellationException;
21
22
import java.util.concurrent.ExecutorService;
34
35
 
35
36
/**
36
37
 * <p>
37
 
 * Implementation of remote command execution using RSE's shell service. 
 
38
 * Implementation of remote command execution using RSE's shell service.
38
39
 * </p>
39
 
 * 
 
40
 *
40
41
 * @author Patrick Tasse
41
42
 * @author Bernd Hufmann
42
43
 */
46
47
    // Constants
47
48
    // ------------------------------------------------------------------------
48
49
 
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$
51
 
    
52
 
    //command delimiter for shell
 
52
 
 
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$
 
55
 
 
56
    /** Command delimiter for shell */
53
57
    public final static String CMD_DELIMITER = "\n"; //$NON-NLS-1$
54
58
 
 
59
    /** Shell "echo" command */
55
60
    public final static String SHELL_ECHO_CMD = " echo "; //$NON-NLS-1$
56
61
 
57
 
    private final static int DEFAULT_TIMEOUT_VALUE = 15000; // in milliseconds
 
62
    /** Default command separator */
 
63
    public final static char CMD_SEPARATOR = ';';
 
64
 
 
65
    /** Default timeout value used for executing commands, in milliseconds */
 
66
    private final static int DEFAULT_TIMEOUT_VALUE = 15000;
58
67
 
59
68
    // ------------------------------------------------------------------------
60
69
    // Attributes
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;
67
 
    
 
77
    private final Random fRandom = new Random(System.currentTimeMillis());
 
78
    private int fReturnValue;
 
79
 
68
80
    // ------------------------------------------------------------------------
69
81
    // Constructors
70
82
    // ------------------------------------------------------------------------
 
83
 
 
84
    /**
 
85
     * Create a new command shell
 
86
     *
 
87
     * @param proxy
 
88
     *            The RSE proxy for this shell
 
89
     */
71
90
    public CommandShell(IRemoteSystemProxy proxy) {
72
91
        fProxy = proxy;
73
92
    }
89
108
        } catch (Exception e) {
90
109
            throw new ExecutionException(Messages.TraceControl_CommandShellError, e);
91
110
        }
92
 
        fBufferReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
 
111
        fInputBufferReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
 
112
        fErrorBufferReader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
93
113
        fIsConnected = true;
94
 
 
95
 
        // Flush Login messages
96
 
        executeCommand(" ", new NullProgressMonitor(), false); //$NON-NLS-1$
97
114
    }
98
115
 
99
116
    /*
104
121
    public void disconnect() {
105
122
        fIsConnected = false;
106
123
        try {
107
 
            fBufferReader.close();
 
124
            fInputBufferReader.close();
 
125
            fErrorBufferReader.close();
108
126
        } catch (IOException e) {
109
127
            // ignore
110
128
        }
130
148
                @Override
131
149
                public CommandResult call() throws IOException, CancellationException {
132
150
                    final ArrayList<String> result = new ArrayList<String>();
133
 
                    int returnValue = 0;
134
151
 
135
152
                    synchronized (fHostShell) {
136
 
                        fHostShell.writeToShell(formatShellCommand(command));
 
153
                        // Initialize return value which will be updated in isAliasEchoResult()
 
154
                        fReturnValue = 0;
 
155
 
 
156
                        int startAlias = fRandom.nextInt();
 
157
                        int endAlias = fRandom.nextInt();
 
158
                        fHostShell.writeToShell(formatShellCommand(command, startAlias, endAlias));
 
159
 
137
160
                        String nextLine;
138
 
                        while ((nextLine = fBufferReader.readLine()) != null) {
 
161
                        boolean isStartFound = false;
 
162
                        while ((nextLine = fInputBufferReader.readLine()) != null) {
139
163
 
140
164
                            if (monitor.isCanceled()) {
141
165
                                flushInput();
142
 
                                throw new CancellationException(); 
143
 
                            }
144
 
 
145
 
                            if (nextLine.contains(DONE_MARKUP_STRING) && nextLine.contains(SHELL_ECHO_CMD)) {
 
166
                                throw new CancellationException();
 
167
                            }
 
168
 
 
169
                            // check if line contains echoed start alias
 
170
                            if (isAliasEchoResult(nextLine, startAlias, true)) {
 
171
                                isStartFound = true;
 
172
                                continue;
 
173
                            }
 
174
 
 
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)) {
146
178
                                break;
147
179
                            }
 
180
 
 
181
                            // Ignore line if
 
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
 
185
                            if (!isStartFound ||
 
186
                                    isCommandEcho(nextLine, command) ||
 
187
                                    nextLine.contains(getEchoResult(endAlias)))
 
188
                            {
 
189
                                continue;
 
190
                            }
 
191
 
 
192
                            // Now it's time add to the result
 
193
                            result.add(nextLine);
148
194
                        }
149
195
 
150
 
                        while ((nextLine = fBufferReader.readLine()) != null) {
151
 
                            // check if job was cancelled
152
 
                            if (monitor.isCanceled()) {
153
 
                                flushInput();
154
 
                                throw new CancellationException(); 
155
 
                            }
 
196
                        // Read any left over output
 
197
                        flushInput();
156
198
 
157
 
                            if (!nextLine.contains(DONE_MARKUP_STRING)) {
158
 
                                result.add(nextLine);
159
 
                            } else {
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);
162
204
                                }
163
 
                                break;
164
205
                            }
165
206
                        }
166
 
 
167
 
                        flushInput();
168
207
                    }
169
 
                    return new CommandResult(returnValue, result.toArray(new String[result.size()]));
 
208
                    return new CommandResult(fReturnValue, result.toArray(new String[result.size()]));
170
209
                }
171
210
            });
172
211
 
184
223
        }
185
224
        throw new ExecutionException(Messages.TraceControl_ShellNotConnected, null);
186
225
    }
187
 
    
 
226
 
188
227
    // ------------------------------------------------------------------------
189
228
    // Helper methods
190
229
    // ------------------------------------------------------------------------
191
230
    /**
192
 
     * Flushes the buffer reader 
 
231
     * Flushes the buffer reader
193
232
     * @throws IOException
194
233
     */
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) {
199
238
                break;
200
239
            }
201
240
        }
202
241
    }
203
 
    
 
242
 
204
243
    /**
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.
207
 
     * 
208
 
     * @param cmd
 
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.
 
247
     *
 
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
210
252
     */
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$
213
255
            return cmd;
 
256
        }
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();
223
267
    }
224
268
 
 
269
    /**
 
270
     * Creates a echo command line in the format: echo <start tag> <alias> <end tag> $?
 
271
     *
 
272
     * @param alias The command alias integer to be included in the echoed message.
 
273
     * @return the echo command line
 
274
     */
 
275
    private static String getEchoCmd(int alias) {
 
276
        return SHELL_ECHO_CMD + getEchoResult(alias) + "$?"; //$NON-NLS-1$
 
277
    }
 
278
 
 
279
    /**
 
280
     * Creates the expected result for a given command alias:
 
281
     * <start tag> <alias> <end tag> $?
 
282
     *
 
283
     * @param alias The command alias integer to be included in the echoed message.
 
284
     * @return the expected echo result
 
285
     */
 
286
    private static String getEchoResult(int alias) {
 
287
        return BEGIN_END_TAG + String.valueOf(alias) + DONE_MARKUP_STRING;
 
288
    }
 
289
 
 
290
    /**
 
291
     * Verifies if given command line contains a command alias echo result.
 
292
     *
 
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>
 
297
     */
 
298
    private boolean isAliasEchoResult(String line, int alias, boolean checkReturnValue) {
 
299
        String expected = getEchoResult(alias);
 
300
        if (line.startsWith(expected)) {
 
301
            if (!checkReturnValue) {
 
302
                try {
 
303
                    int k = Integer.valueOf(line.substring(expected.length()));
 
304
                    fReturnValue = k;
 
305
                } catch (NumberFormatException e) {
 
306
                    // do nothing
 
307
                }
 
308
            }
 
309
            return true;
 
310
        }
 
311
        int index = line.indexOf(expected);
 
312
        if (index > 0) {
 
313
            if (line.indexOf(SHELL_ECHO_CMD) == -1) {
 
314
                return true;
 
315
            }
 
316
        }
 
317
 
 
318
        return false;
 
319
    }
 
320
 
 
321
    /**
 
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.
 
326
     *
 
327
     * @param line
 
328
     *            The output line to verify
 
329
     * @param cmd
 
330
     *            The command executed
 
331
     * @return <code>true</code> if it's an echoed command line else
 
332
     *         <code>false</code>
 
333
     */
 
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);
 
341
    }
225
342
}