Bug 448058: Replace RSE by org.eclipse.remote
[deliverable/tracecompass.git] / org.eclipse.tracecompass.lttng2.control.ui / src / org / eclipse / tracecompass / internal / lttng2 / control / ui / views / remote / CommandShell.java
CommitLineData
eb1bab5b 1/**********************************************************************
60ae41e1 2 * Copyright (c) 2012, 2014 Ericsson
ea21cd65 3 *
eb1bab5b
BH
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
ea21cd65
AM
8 *
9 * Contributors:
eb1bab5b
BH
10 * Patrick Tasse - Initial API and implementation
11 * Bernd Hufmann - Updated using Executor Framework
b732adaa 12 * Markus Schorn - Bug 448058: Use org.eclipse.remote in favor of RSE
eb1bab5b 13 **********************************************************************/
9bc60be7 14package org.eclipse.tracecompass.internal.lttng2.control.ui.views.remote;
eb1bab5b 15
eb1bab5b 16import java.io.IOException;
eb1bab5b 17import java.util.concurrent.Callable;
eb1bab5b
BH
18import java.util.concurrent.ExecutorService;
19import java.util.concurrent.Executors;
20import java.util.concurrent.FutureTask;
21import java.util.concurrent.TimeUnit;
22import java.util.concurrent.TimeoutException;
23
24import org.eclipse.core.commands.ExecutionException;
25import org.eclipse.core.runtime.IProgressMonitor;
26import org.eclipse.core.runtime.NullProgressMonitor;
b732adaa
MS
27import org.eclipse.core.runtime.OperationCanceledException;
28import org.eclipse.remote.core.IRemoteConnection;
29import org.eclipse.remote.core.IRemoteProcess;
30import org.eclipse.remote.core.IRemoteProcessBuilder;
9bc60be7
AM
31import org.eclipse.tracecompass.internal.lttng2.control.ui.views.messages.Messages;
32import org.eclipse.tracecompass.internal.lttng2.control.ui.views.preferences.ControlPreferences;
eb1bab5b
BH
33
34/**
eb1bab5b 35 * <p>
b732adaa 36 * Implementation of remote command execution using IRemoteConnection.
eb1bab5b 37 * </p>
cfdb727a 38 *
dbd4432d
BH
39 * @author Patrick Tasse
40 * @author Bernd Hufmann
eb1bab5b
BH
41 */
42public class CommandShell implements ICommandShell {
43
44 // ------------------------------------------------------------------------
45 // Constants
46 // ------------------------------------------------------------------------
47
b732adaa
MS
48 private static final String BEGIN_TAG = "org.eclipse.tracecompass-BEGIN-TAG:"; //$NON-NLS-1$
49 private static final String END_TAG = "org.eclipse.tracecompass-END-TAG:"; //$NON-NLS-1$
50 private static final String RSE_ADAPTER_ID = "org.eclipse.ptp.remote.RSERemoteServices"; //$NON-NLS-1$
51 private static final String SHELL_ECHO_CMD = "echo "; //$NON-NLS-1$
52 private static final char CMD_SEPARATOR = ';';
53 private static final String CMD_RESULT_VAR = " $?"; //$NON-NLS-1$
eb1bab5b 54
eb1bab5b
BH
55 // ------------------------------------------------------------------------
56 // Attributes
57 // ------------------------------------------------------------------------
b732adaa 58 private IRemoteConnection fConnection = null;
ea21cd65 59 private final ExecutorService fExecutor = Executors.newFixedThreadPool(1);
b732adaa 60 private int fBackedByShell;
ea21cd65 61
eb1bab5b
BH
62 // ------------------------------------------------------------------------
63 // Constructors
64 // ------------------------------------------------------------------------
ea21cd65
AM
65
66 /**
67 * Create a new command shell
68 *
b732adaa 69 * @param connection the remote connection for this shell
ea21cd65 70 */
b732adaa
MS
71 public CommandShell(IRemoteConnection connection) {
72 fConnection = connection;
eb1bab5b
BH
73 }
74
75 // ------------------------------------------------------------------------
76 // Operations
77 // ------------------------------------------------------------------------
11252342 78
eb1bab5b
BH
79 @Override
80 public void connect() throws ExecutionException {
eb1bab5b
BH
81 }
82
eb1bab5b
BH
83 @Override
84 public void disconnect() {
b732adaa 85 fExecutor.shutdown();
eb1bab5b
BH
86 }
87
eb1bab5b 88 @Override
b732adaa
MS
89 public ICommandResult executeCommand(final String command, final IProgressMonitor monitor) throws ExecutionException {
90 if (fConnection.isOpen()) {
e0838ca1 91 FutureTask<CommandResult> future = new FutureTask<>(new Callable<CommandResult>() {
eb1bab5b 92 @Override
b732adaa
MS
93 public CommandResult call() throws IOException, InterruptedException {
94 if (monitor == null || !monitor.isCanceled()) {
95 final boolean wrapCommand =
96 RSE_ADAPTER_ID.equals(fConnection.getRemoteServices().getId())
97 && isBackedByShell();
98 IRemoteProcess process = startRemoteProcess(wrapCommand, command);
99 InputReader stdout = new InputReader(process.getInputStream());
100 InputReader stderr = new InputReader(process.getErrorStream());
101
102 try {
103 stdout.waitFor(monitor);
104 stderr.waitFor(monitor);
105 if (monitor == null || !monitor.isCanceled()) {
106 return createResult(wrapCommand, process.waitFor(), stdout.toString(), stderr.toString());
1a18ada9 107 }
b732adaa
MS
108 } catch (OperationCanceledException e) {
109 } catch (InterruptedException e) {
110 return new CommandResult(1, new String[0], new String[] {e.getMessage()});
111 } finally {
112 stdout.stop();
113 stderr.stop();
114 process.destroy();
d6fc6e1b 115 }
eb1bab5b 116 }
b732adaa 117 return new CommandResult(1, new String[0], new String[] {"cancelled"}); //$NON-NLS-1$
eb1bab5b
BH
118 }
119 });
120
121 fExecutor.execute(future);
122
123 try {
4bdf5f96 124 return future.get(ControlPreferences.getInstance().getCommandTimeout(), TimeUnit.SECONDS);
eb1bab5b
BH
125 } catch (java.util.concurrent.ExecutionException ex) {
126 throw new ExecutionException(Messages.TraceControl_ExecutionFailure, ex);
127 } catch (InterruptedException ex) {
128 throw new ExecutionException(Messages.TraceControl_ExecutionCancelled, ex);
129 } catch (TimeoutException ex) {
130 throw new ExecutionException(Messages.TraceControl_ExecutionTimeout, ex);
b732adaa
MS
131 } finally {
132 future.cancel(true);
eb1bab5b
BH
133 }
134 }
135 throw new ExecutionException(Messages.TraceControl_ShellNotConnected, null);
136 }
cfdb727a 137
b732adaa
MS
138 private IRemoteProcess startRemoteProcess(boolean wrapCommand, String command) throws IOException {
139 String outputCommand = command;
140 if (wrapCommand) {
141 StringBuilder formattedCommand = new StringBuilder();
142 formattedCommand.append(SHELL_ECHO_CMD).append(BEGIN_TAG);
143 formattedCommand.append(CMD_SEPARATOR);
144 formattedCommand.append(command);
145 formattedCommand.append(CMD_SEPARATOR);
146 formattedCommand.append(SHELL_ECHO_CMD).append(END_TAG).append(CMD_RESULT_VAR);
147 outputCommand = formattedCommand.toString();
148 }
149
150 String[] args = outputCommand.trim().split("\\s+"); //$NON-NLS-1$
151 return fConnection.getProcessBuilder(args).start();
152 }
153
154 private boolean isBackedByShell() throws InterruptedException {
155 if (fBackedByShell == 0) {
156 String cmd= SHELL_ECHO_CMD + BEGIN_TAG + CMD_SEPARATOR + SHELL_ECHO_CMD + END_TAG;
157 IRemoteProcessBuilder pb = fConnection.getProcessBuilder(cmd.trim().split("\\s+")); //$NON-NLS-1$
158 pb.redirectErrorStream(true);
159 IRemoteProcess process = null;
160 InputReader reader = null;
161 try {
162 process = pb.start();
163 reader = new InputReader(process.getInputStream());
164 reader.waitFor(new NullProgressMonitor());
165 process.waitFor();
166
167 fBackedByShell = -1;
168 String result= reader.toString();
169 int pos = result.indexOf(BEGIN_TAG, skipEchoBeginTag(result));
170 if (pos >= 0 && result.substring(pos + BEGIN_TAG.length()).trim().startsWith(END_TAG)) {
171 fBackedByShell = 1;
172 }
173 } catch (IOException e) {
174 // On Windows, cannot start built-in echo command
175 fBackedByShell = -1;
176 } finally {
177 if (process != null) {
178 process.destroy();
179 }
180 if (reader != null) {
181 reader.stop();
182 }
183 }
184 }
185 return fBackedByShell == 1;
186 }
187
eb1bab5b
BH
188 // ------------------------------------------------------------------------
189 // Helper methods
190 // ------------------------------------------------------------------------
b732adaa
MS
191
192 private static CommandResult createResult(boolean isWrapped, int origResult, String origStdout, String origStderr) {
193 final int result;
194 final String stdout, stderr;
195 if (isWrapped) {
196 String[] holder = {origStdout};
197 result = unwrapOutput(holder);
198 stdout = holder[0];
199 // Workaround if error stream is not available and stderr output is written
200 // in standard output above. This is true for the SshTerminalShell implementation.
201 stderr = origStderr.isEmpty() ? stdout : origStderr;
202 } else {
203 result = origResult;
204 stdout = origStdout;
205 stderr = origStderr;
eb1bab5b 206 }
b732adaa
MS
207
208 String[] output = splitLines(stdout);
209 String[] error = result == 0 ? null : splitLines(stderr);
210 return new CommandResult(result, output, error);
eb1bab5b 211 }
cfdb727a 212
b732adaa
MS
213 private static String[] splitLines(String output) {
214 if (output == null) {
215 return null;
cfdb727a 216 }
b732adaa 217 return output.split("\\r?\\n"); //$NON-NLS-1$
eb1bab5b 218 }
ea21cd65 219
b732adaa
MS
220 private static int unwrapOutput(String[] outputHolder) {
221 String output = outputHolder[0];
222 int begin = skipEchoBeginTag(output);
223 begin = output.indexOf(BEGIN_TAG, begin);
d6fc6e1b 224
b732adaa
MS
225 if (begin < 0) {
226 outputHolder[0] = ""; //$NON-NLS-1$
227 return 1;
228 }
ea21cd65 229
b732adaa
MS
230 begin += BEGIN_TAG.length();
231 int end = output.indexOf(END_TAG, begin);
232 if (end < 0) {
233 outputHolder[0] = output.substring(begin).trim();
234 return 1;
235 }
236
237 outputHolder[0] = output.substring(begin, end).trim();
238 String tail = output.substring(end + END_TAG.length()).trim();
239 int numEnd;
240 for (numEnd = 0; numEnd < tail.length(); numEnd++) {
241 if (!Character.isDigit(tail.charAt(numEnd))) {
242 break;
d6fc6e1b 243 }
ea21cd65 244 }
b732adaa
MS
245 try {
246 return Integer.parseInt(tail.substring(0, numEnd));
247 } catch (NumberFormatException e) {
248 return 1;
d6fc6e1b 249 }
d6fc6e1b 250 }
ea21cd65 251
b732adaa
MS
252 private static int skipEchoBeginTag(String output) {
253 final String searchFor = SHELL_ECHO_CMD + BEGIN_TAG;
254 int begin = 0;
255 for(;;) {
256 int i= output.indexOf(searchFor, begin);
257 if (i >= begin) {
258 begin = i + searchFor.length();
259 } else {
260 return begin;
261 }
262 }
d6fc6e1b 263 }
eb1bab5b 264}
This page took 0.06355 seconds and 5 git commands to generate.