diff --git a/bundles/org.eclipse.remote.core/plugin.xml b/bundles/org.eclipse.remote.core/plugin.xml index bedfc2973f3..51b916ebf9e 100644 --- a/bundles/org.eclipse.remote.core/plugin.xml +++ b/bundles/org.eclipse.remote.core/plugin.xml @@ -33,6 +33,16 @@ factory="org.eclipse.remote.internal.core.services.local.LocalProcessService$Factory" service="org.eclipse.remote.core.IRemoteProcessService"> + + + + diff --git a/bundles/org.eclipse.remote.core/schema/remoteServices.exsd b/bundles/org.eclipse.remote.core/schema/remoteServices.exsd index ad5a5424dd4..a8e42467cb8 100644 --- a/bundles/org.eclipse.remote.core/schema/remoteServices.exsd +++ b/bundles/org.eclipse.remote.core/schema/remoteServices.exsd @@ -21,6 +21,7 @@ + @@ -173,6 +174,46 @@ + + + + This is a service that implements the given service interface for processes of a given connection type. + + + + + + + The connection type for the connections that this service applies to. + + + + + + + + + + The interface class that this service implements. The service is found by calling the getService() method on the IRemoteProcess object with this interface class as the parameter. + + + + + + + + + + The factory class used to instantiate the service. + + + + + + + + + diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/AbstractRemoteProcess.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/AbstractRemoteProcess.java deleted file mode 100644 index a3a8972dc2e..00000000000 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/AbstractRemoteProcess.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - Initial API and implementation - *******************************************************************************/ -package org.eclipse.remote.core; - -import java.io.InputStream; -import java.io.OutputStream; - -/** - * Abstract base class for remote processes. Implementors can use this class to provide a default remote process implementation. - */ -public abstract class AbstractRemoteProcess extends Process implements IRemoteProcess { - /* - * (non-Javadoc) - * - * @see java.lang.Process#destroy() - */ - @Override - public void destroy() { - // Nothing - } - - /* - * (non-Javadoc) - * - * @see java.lang.Process#exitValue() - */ - @Override - public int exitValue() { - return 0; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Process#getErrorStream() - */ - @Override - public InputStream getErrorStream() { - return null; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Process#getInputStream() - */ - @Override - public InputStream getInputStream() { - return null; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Process#getOutputStream() - */ - @Override - public OutputStream getOutputStream() { - return null; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Process#waitFor() - */ - @Override - public int waitFor() throws InterruptedException { - return 0; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.remote.core.IRemoteProcess#isCompleted() - */ - @Override - public boolean isCompleted() { - return true; - } -} diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/AbstractRemoteProcessBuilder.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/AbstractRemoteProcessBuilder.java index 839b2a7fa13..1b208f3b218 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/AbstractRemoteProcessBuilder.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/AbstractRemoteProcessBuilder.java @@ -25,15 +25,24 @@ import org.eclipse.core.filesystem.IFileStore; */ public abstract class AbstractRemoteProcessBuilder implements IRemoteProcessBuilder { private List fCommandArgs; - private IFileStore fRemoteDir = null; - private boolean fRedirectErrorStream = false; + private IFileStore fRemoteDir; + private boolean fRedirectErrorStream; - public AbstractRemoteProcessBuilder(List command) { + private final IRemoteConnection fConnection; + + /** + * @since 2.0 + */ + public AbstractRemoteProcessBuilder(IRemoteConnection connection, List command) { fCommandArgs = command; + fConnection = connection; } - public AbstractRemoteProcessBuilder(String... command) { - this(Arrays.asList(command)); + /** + * @since 2.0 + */ + public AbstractRemoteProcessBuilder(IRemoteConnection connection, String... command) { + this(connection, Arrays.asList(command)); } /* @@ -173,4 +182,17 @@ public abstract class AbstractRemoteProcessBuilder implements IRemoteProcessBuil } return res.toString(); } + + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcessBuilder#getRemoteConnection() + */ + /** + * @since 2.0 + */ + @Override + public IRemoteConnection getRemoteConnection() { + return fConnection; + } } diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteConnectionType.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteConnectionType.java index 94caa2e9f03..19196f0fe0a 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteConnectionType.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteConnectionType.java @@ -110,6 +110,15 @@ public interface IRemoteConnectionType { */ boolean hasConnectionService(Class service); + /** + * Do processes created by this connection type support the given service. + * + * @param service + * the service to be tested + * @return true if processes created by this connection type support this service + */ + boolean hasProcessService(Class service); + /** * Gets the remote connection corresponding to the supplied name. * diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcess.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcess.java index c5e86d885fd..ff5fbbb433d 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcess.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcess.java @@ -14,21 +14,33 @@ import java.io.InputStream; import java.io.OutputStream; /** - * Abstraction of a process running on a remote system. Remote process are created using the {@link IRemoteProcessBuilder} - * interface. + * Represents a process running on a remote system. Remote process are created using the {@link IRemoteProcessBuilder} interface. */ public interface IRemoteProcess { + /** + * The interface that is extend by services provided for this remote connection. + * + * @since 2.0 + */ + interface Service { + IRemoteProcess getRemoteProcess(); + + interface Factory { + T getService(IRemoteProcess remoteProcess, Class service); + } + } + /** * Terminate the process */ - public void destroy(); + void destroy(); /** * Returns the exit value for the process * * @return the exit value */ - public int exitValue(); + int exitValue(); /** * Gets the error output stream of the process @@ -36,7 +48,7 @@ public interface IRemoteProcess { * @return the output stream connected to the standard * error of the process */ - public InputStream getErrorStream(); + InputStream getErrorStream(); /** * Gets an InputStream which can be used to read the standard output stream of the process @@ -44,7 +56,7 @@ public interface IRemoteProcess { * @return the input stream connected to the standard * output of the process */ - public InputStream getInputStream(); + InputStream getInputStream(); /** * Gets an output stream which can be used to write to the standard input stream of the process @@ -52,7 +64,27 @@ public interface IRemoteProcess { * @return the output stream connected to the standard * input of the process */ - public OutputStream getOutputStream(); + OutputStream getOutputStream(); + + /** + * Get the service for this remote process that implements the given interface. + * + * @param service + * the interface the required service must implements + * @return the desired service or null if there is no such service available + * @since 2.0 + */ + T getService(Class service); + + /** + * Does this remote process support the given service. + * + * @param service + * The service to be tested + * @return true if this connection supports the service + * @since 2.0 + */ + boolean hasService(Class service); /** * Wait until the process has terminated @@ -62,12 +94,28 @@ public interface IRemoteProcess { * if the current thread is * interrupted by another thread while it is waiting */ - public int waitFor() throws InterruptedException; + int waitFor() throws InterruptedException; /** * Check if the remote process has completed * * @return true if remote process has completed */ - public boolean isCompleted(); + boolean isCompleted(); + + /** + * Get the connection that is used by this process + * + * @return connection used by this process + * @since 2.0 + */ + IRemoteConnection getRemoteConnection(); + + /** + * Get the process builder used to create this process + * + * @return process builder used to create this process + * @since 2.0 + */ + IRemoteProcessBuilder getProcessBuilder(); } diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessBuilder.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessBuilder.java index 9a8f966e4a5..d46bdd365f5 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessBuilder.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessBuilder.java @@ -46,7 +46,7 @@ public interface IRemoteProcessBuilder { * * @return a list containing the program and arguments */ - public List command(); + List command(); /** * Sets this process builder's operating system program and arguments. @@ -54,7 +54,7 @@ public interface IRemoteProcessBuilder { * @param command * @return This process builder */ - public IRemoteProcessBuilder command(List command); + IRemoteProcessBuilder command(List command); /** * Sets this process builder's operating system program and arguments. @@ -62,14 +62,14 @@ public interface IRemoteProcessBuilder { * @param command * @return this process builder */ - public IRemoteProcessBuilder command(String... command); + IRemoteProcessBuilder command(String... command); /** * Returns this process builder's working directory. * * @return an IFileStore reference to the working directory */ - public IFileStore directory(); + IFileStore directory(); /** * Sets this process builder's working directory. @@ -77,7 +77,7 @@ public interface IRemoteProcessBuilder { * @param directory * @return This process builder */ - public IRemoteProcessBuilder directory(IFileStore directory); + IRemoteProcessBuilder directory(IFileStore directory); /** * Returns a string map view of this process builder's environment. The @@ -85,7 +85,7 @@ public interface IRemoteProcessBuilder { * * @return the process builder's environment */ - public Map environment(); + Map environment(); /** * Get the flags that are supported by this process builder. @@ -93,7 +93,7 @@ public interface IRemoteProcessBuilder { * @return bitwise-or of the supported flags * @since 5.0 */ - public int getSupportedFlags(); + int getSupportedFlags(); /** * Tells whether this process builder merges standard error and standard @@ -101,7 +101,7 @@ public interface IRemoteProcessBuilder { * * @return true if standard error and standard output will be merged */ - public boolean redirectErrorStream(); + boolean redirectErrorStream(); /** * Sets this process builder's redirectErrorStream property. @@ -109,7 +109,7 @@ public interface IRemoteProcessBuilder { * @param redirectErrorStream * @return This process builder */ - public IRemoteProcessBuilder redirectErrorStream(boolean redirectErrorStream); + IRemoteProcessBuilder redirectErrorStream(boolean redirectErrorStream); /** * Starts a new process using the attributes of this process builder. @@ -117,7 +117,7 @@ public interface IRemoteProcessBuilder { * @return remote process object * @throws IOException */ - public IRemoteProcess start() throws IOException; + IRemoteProcess start() throws IOException; /** * Starts a new process using the attributes of this process builder. The @@ -139,5 +139,13 @@ public interface IRemoteProcessBuilder { * @throws IOException * @since 5.0 */ - public IRemoteProcess start(int flags) throws IOException; + IRemoteProcess start(int flags) throws IOException; + + /** + * Get the connection that will be used by this process builder to create remote processes. + * + * @return connection used to create remote processes + * @since 2.0 + */ + IRemoteConnection getRemoteConnection(); } \ No newline at end of file diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessControlService.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessControlService.java new file mode 100644 index 00000000000..65b2d4beed7 --- /dev/null +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessControlService.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - initial API and implementation + *******************************************************************************/ +package org.eclipse.remote.core; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A service to control and report on the state of a process. + * + * @since 2.0 + */ +public interface IRemoteProcessControlService extends IRemoteProcess.Service { + /** + * Terminate the process + */ + public void destroy(); + + /** + * Returns the exit value for the process + * + * @return the exit value + */ + public int exitValue(); + + /** + * Gets the error output stream of the process + * + * @return the output stream connected to the standard + * error of the process + */ + public InputStream getErrorStream(); + + /** + * Gets an InputStream which can be used to read the standard output stream of the process + * + * @return the input stream connected to the standard + * output of the process + */ + public InputStream getInputStream(); + + /** + * Gets an output stream which can be used to write to the standard input stream of the process + * + * @return the output stream connected to the standard + * input of the process + */ + public OutputStream getOutputStream(); + + /** + * Wait until the process has terminated + * + * @return the exit value of the process + * @throws InterruptedException + * if the current thread is + * interrupted by another thread while it is waiting + */ + public int waitFor() throws InterruptedException; + + /** + * Check if the remote process has completed + * + * @return true if remote process has completed + */ + public boolean isCompleted(); +} diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessSignalService.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessSignalService.java new file mode 100644 index 00000000000..af09b3a3599 --- /dev/null +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessSignalService.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2015 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - Initial API and implementation + *******************************************************************************/ +package org.eclipse.remote.core; + +import org.eclipse.remote.core.exception.RemoteConnectionException; + +/** + * A service abstraction for signals. + * + * @since 2.0 + */ +public interface IRemoteProcessSignalService extends IRemoteProcess.Service { + public static final int HUP = 1; + public static final int INT = 2; + public static final int QUIT = 3; + public static final int ILL = 4; + public static final int ABRT = 6; + public static final int FPE = 8; + public static final int KILL = 9; + public static final int SEGV = 11; + public static final int PIPE = 13; + public static final int ALRM = 14; + public static final int TERM = 15; + public static final int STOP = 17; + public static final int TSTP = 18; + public static final int CONT = 19; + public static final int USR1 = 30; + public static final int USR2 = 31; + + /** + * Send a signal to the remote process. + * + * @param signal + * signal to send. + * @throws RemoteConnectionException + * if the underlying connection fails + * @since 2.0 + */ + void sendSignal(int signal) throws RemoteConnectionException; +} diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessTerminalService.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessTerminalService.java new file mode 100644 index 00000000000..6c34a207ecd --- /dev/null +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/core/IRemoteProcessTerminalService.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2015 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - Initial API and implementation + *******************************************************************************/ +package org.eclipse.remote.core; + +/** + * A service abstraction for remote terminals. + * + * @since 2.0 + */ +public interface IRemoteProcessTerminalService extends IRemoteProcess.Service { + /** + * Change the terminal window dimension interactively. Refer to RFC 4254 6.7. Window Dimension Change Message. The character/row + * dimensions override the pixel dimensions (when nonzero). Pixel dimensions refer to the drawable area of the window. + * + * @param cols + * terminal width in characters + * @param rows + * terminal height in characters + * @param width + * terminal width in pixels + * @param height + * terminal height in pixels + */ + void setTerminalSize(int cols, int rows, int width, int height); +} diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteConnectionType.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteConnectionType.java index a07dacaa239..d62cc98cd86 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteConnectionType.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteConnectionType.java @@ -22,6 +22,7 @@ import org.eclipse.equinox.security.storage.ISecurePreferences; import org.eclipse.remote.core.IRemoteConnection; import org.eclipse.remote.core.IRemoteConnectionType; import org.eclipse.remote.core.IRemoteConnectionWorkingCopy; +import org.eclipse.remote.core.IRemoteProcess; import org.eclipse.remote.core.IRemoteServicesManager; import org.eclipse.remote.core.RemoteConnectionChangeEvent; import org.eclipse.remote.core.exception.ConnectionExistsException; @@ -204,6 +205,44 @@ public class RemoteConnectionType implements IRemoteConnectionType { return serviceDefinitionMap.get(service.getName()) != null; } + /** + * Called from the remote process to get a service object for that process. + * + * @param process + * the process to which the service applies + * @param service + * the interface the service must implement + * @return the service object + * @throws CoreException + */ + public T getProcessService(IRemoteProcess process, Class service) { + // Both top level and connection services are stored in the serviceDefinitionMap. + // In theory the two sets of interfaces can't collide. + IConfigurationElement ce = serviceDefinitionMap.get(service.getName()); + if (ce != null) { + try { + IRemoteProcess.Service.Factory factory = (IRemoteProcess.Service.Factory) ce.createExecutableExtension("factory"); //$NON-NLS-1$ + if (factory != null) { + return factory.getService(process, service); + } + } catch (CoreException e) { + RemoteCorePlugin.log(e.getStatus()); + } + } + + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteConnectionType#hasProcessService(java.lang.Class) + */ + @Override + public boolean hasProcessService(Class service) { + return serviceDefinitionMap.get(service.getName()) != null; + } + /** * Called from the remote service manager to register a service extension for * this remote services implementation diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteProcess.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteProcess.java new file mode 100644 index 00000000000..d5ecde6c746 --- /dev/null +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteProcess.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - Initial API and implementation + *******************************************************************************/ +package org.eclipse.remote.internal.core; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.remote.core.IRemoteConnection; +import org.eclipse.remote.core.IRemoteProcess; +import org.eclipse.remote.core.IRemoteProcessBuilder; +import org.eclipse.remote.core.IRemoteProcessControlService; + +/** + * Standard root class for remote processes. + */ +public class RemoteProcess extends Process implements IRemoteProcess { + private final Map servicesMap = new HashMap<>(); + private final IRemoteConnection connection; + private final IRemoteProcessBuilder builder; + + /** + * @since 2.0 + */ + public RemoteProcess(IRemoteConnection connection, IRemoteProcessBuilder builder) { + this.connection = connection; + this.builder = builder; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Process#destroy() + */ + @Override + public void destroy() { + IRemoteProcessControlService controlService = getService(IRemoteProcessControlService.class); + if (controlService != null) { + controlService.destroy(); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Process#exitValue() + */ + @Override + public int exitValue() { + IRemoteProcessControlService controlService = getService(IRemoteProcessControlService.class); + if (controlService != null) { + return controlService.exitValue(); + } + return 0; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Process#getErrorStream() + */ + @Override + public InputStream getErrorStream() { + IRemoteProcessControlService controlService = getService(IRemoteProcessControlService.class); + if (controlService != null) { + return controlService.getErrorStream(); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Process#getInputStream() + */ + @Override + public InputStream getInputStream() { + IRemoteProcessControlService controlService = getService(IRemoteProcessControlService.class); + if (controlService != null) { + return controlService.getInputStream(); + } + return null; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Process#getOutputStream() + */ + @Override + public OutputStream getOutputStream() { + IRemoteProcessControlService controlService = getService(IRemoteProcessControlService.class); + if (controlService != null) { + return controlService.getOutputStream(); + } + return null; + } + + /** + * @since 2.0 + */ + @SuppressWarnings("unchecked") + @Override + public T getService(Class service) { + String serviceName = service.getName(); + Object obj = servicesMap.get(serviceName); + if (obj == null) { + obj = getConnectionType().getProcessService(this, service); + if (obj != null) { + servicesMap.put(serviceName, obj); + } + } + + return (T) obj; + } + + /** + * @since 2.0 + */ + @Override + public boolean hasService(Class service) { + return servicesMap.get(service.getName()) != null || getConnectionType().hasProcessService(service); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Process#waitFor() + */ + @Override + public int waitFor() throws InterruptedException { + IRemoteProcessControlService controlService = getService(IRemoteProcessControlService.class); + if (controlService != null) { + return controlService.waitFor(); + } + return 0; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcess#isCompleted() + */ + @Override + public boolean isCompleted() { + IRemoteProcessControlService controlService = getService(IRemoteProcessControlService.class); + if (controlService != null) { + return controlService.isCompleted(); + } + return true; + } + + /** + * @since 2.0 + */ + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcess#getRemoteConnection() + */ + @Override + public IRemoteConnection getRemoteConnection() { + return connection; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcess#getProcessBuilder() + */ + @Override + public IRemoteProcessBuilder getProcessBuilder() { + return builder; + } + + private RemoteConnectionType getConnectionType() { + return (RemoteConnectionType) getRemoteConnection().getConnectionType(); + } +} diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteServicesManager.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteServicesManager.java index f7de07d4b6a..5b1008593c2 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteServicesManager.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/RemoteServicesManager.java @@ -83,7 +83,7 @@ public class RemoteServicesManager implements IRemoteServicesManager { for (IExtension ext : point.getExtensions()) { for (IConfigurationElement ce : ext.getConfigurationElements()) { String name = ce.getName(); - if (name.equals("connectionTypeService") || name.equals("connectionService")) { //$NON-NLS-1$ //$NON-NLS-2$ + if (name.equals("connectionTypeService") || name.equals("connectionService") || name.equals("processService")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ String id = ce.getAttribute("connectionTypeId"); //$NON-NLS-1$ RemoteConnectionType services = connectionTypeMap.get(id); if (services != null) { diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcess.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcess.java index 4440420d3cc..806cdee790d 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcess.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcess.java @@ -16,11 +16,13 @@ import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; -import org.eclipse.remote.core.AbstractRemoteProcess; +import org.eclipse.remote.core.IRemoteProcess; +import org.eclipse.remote.core.IRemoteProcessControlService; -public class LocalProcess extends AbstractRemoteProcess { +public class LocalProcess implements IRemoteProcessControlService { private static int refCount = 0; + private final IRemoteProcess remoteProcess; private final Process localProcess; private InputStream procStdout; private InputStream procStderr; @@ -29,12 +31,35 @@ public class LocalProcess extends AbstractRemoteProcess { private final Thread completedChecker; private volatile boolean isCompleted; + public static class Factory implements IRemoteProcess.Service.Factory { + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcess.Service.Factory#getService(org.eclipse.remote.core.IRemoteProcess, + * java.lang.Class) + */ + @SuppressWarnings("unchecked") + @Override + public T getService(IRemoteProcess remoteProcess, Class service) { + // This little trick creates an instance of this class + // then for each interface it implements, it returns the same object. + // This works because the connection caches the service so only one gets created. + // As a side effect, it makes this class a service too which can be used + // by the this plug-in + if (LocalProcess.class.equals(service)) { + return (T) new LocalProcess(remoteProcess); + } + if (IRemoteProcessControlService.class.equals(service)) { + return (T) remoteProcess.getService(LocalProcess.class); + } + return null; + } + } + /** * Thread to merge stdout and stderr. Keeps refcount so that output stream * is not closed too early. * - * @author greg - * */ private class ProcOutputMerger implements Runnable { private final static int BUF_SIZE = 8192; @@ -86,23 +111,28 @@ public class LocalProcess extends AbstractRemoteProcess { } } - public LocalProcess(Process proc, boolean merge) throws IOException { - localProcess = proc; + public LocalProcess(IRemoteProcess process) { + remoteProcess = process; + localProcess = ((LocalProcessBuilder) process.getProcessBuilder()).getProcess(); - if (merge) { - PipedOutputStream pipedOutput = new PipedOutputStream(); + try { + if (process.getProcessBuilder().redirectErrorStream()) { + PipedOutputStream pipedOutput = new PipedOutputStream(); - procStderr = new NullInputStream(); - procStdout = new PipedInputStream(pipedOutput); + procStderr = new NullInputStream(); + procStdout = new PipedInputStream(pipedOutput); - stderrReader = new Thread(new ProcOutputMerger(proc.getErrorStream(), pipedOutput)); - stdoutReader = new Thread(new ProcOutputMerger(proc.getInputStream(), pipedOutput)); + stderrReader = new Thread(new ProcOutputMerger(localProcess.getErrorStream(), pipedOutput)); + stdoutReader = new Thread(new ProcOutputMerger(localProcess.getInputStream(), pipedOutput)); - stderrReader.start(); - stdoutReader.start(); - } else { - procStderr = localProcess.getErrorStream(); - procStdout = localProcess.getInputStream(); + stderrReader.start(); + stdoutReader.start(); + } else { + procStderr = localProcess.getErrorStream(); + procStdout = localProcess.getInputStream(); + } + } catch (IOException e) { + localProcess.destroy(); } completedChecker = new Thread(new Runnable() { @@ -185,10 +215,15 @@ public class LocalProcess extends AbstractRemoteProcess { /* * (non-Javadoc) * - * @see org.eclipse.remote.core.AbstractRemoteProcess#isCompleted() + * @see org.eclipse.remote.core.RemoteProcess#isCompleted() */ @Override public boolean isCompleted() { return isCompleted; } + + @Override + public IRemoteProcess getRemoteProcess() { + return remoteProcess; + } } diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessBuilder.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessBuilder.java index f26b3a57beb..022f453f94e 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessBuilder.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessBuilder.java @@ -30,8 +30,10 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.remote.core.AbstractRemoteProcessBuilder; import org.eclipse.remote.core.IProcessFactory; +import org.eclipse.remote.core.IRemoteConnection; import org.eclipse.remote.core.IRemoteProcess; import org.eclipse.remote.internal.core.RemoteCorePlugin; +import org.eclipse.remote.internal.core.RemoteProcess; public class LocalProcessBuilder extends AbstractRemoteProcessBuilder { private static final String EXTENSION_POINT_ID = "processFactory"; //$NON-NLS-1$ @@ -40,14 +42,16 @@ public class LocalProcessBuilder extends AbstractRemoteProcessBuilder { private final IProcessFactory fProcessFactory; private final Map fRemoteEnv = new HashMap(); - public LocalProcessBuilder(List command) { - super(command); + private Process localProcess; + + public LocalProcessBuilder(IRemoteConnection connection, List command) { + super(connection, command); fRemoteEnv.putAll(System.getenv()); fProcessFactory = getProcessFactory(); } - public LocalProcessBuilder(String... command) { - this(Arrays.asList(command)); + public LocalProcessBuilder(IRemoteConnection connection, String... command) { + this(connection, Arrays.asList(command)); } /* @@ -104,18 +108,21 @@ public class LocalProcessBuilder extends AbstractRemoteProcessBuilder { for (Entry entry : environment().entrySet()) { environmentArray[index++] = entry.getKey() + "=" + entry.getValue(); //$NON-NLS-1$ } - Process localProc; if (directory() != null) { try { - localProc = fProcessFactory.exec(commandArray, environmentArray, + localProcess = fProcessFactory.exec(commandArray, environmentArray, directory().toLocalFile(EFS.NONE, new NullProgressMonitor())); } catch (CoreException e) { throw new IOException(e.getMessage()); } } else { - localProc = fProcessFactory.exec(commandArray, environmentArray); + localProcess = fProcessFactory.exec(commandArray, environmentArray); } - return new LocalProcess(localProc, redirectErrorStream()); + return new RemoteProcess(getRemoteConnection(), this); + } + + public Process getProcess() { + return localProcess; } private IProcessFactory getProcessFactory() { diff --git a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessService.java b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessService.java index 6bdb204c1ee..7dac4f81b5b 100644 --- a/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessService.java +++ b/bundles/org.eclipse.remote.core/src/org/eclipse/remote/internal/core/services/local/LocalProcessService.java @@ -46,12 +46,12 @@ public class LocalProcessService implements IRemoteProcessService { @Override public IRemoteProcessBuilder getProcessBuilder(List command) { - return new LocalProcessBuilder(command); + return new LocalProcessBuilder(remoteConnection, command); } @Override public IRemoteProcessBuilder getProcessBuilder(String... command) { - return new LocalProcessBuilder(command); + return new LocalProcessBuilder(remoteConnection, command); } @Override diff --git a/bundles/org.eclipse.remote.jsch.core/plugin.xml b/bundles/org.eclipse.remote.jsch.core/plugin.xml index 1c585d04b9d..70b0eb06389 100644 --- a/bundles/org.eclipse.remote.jsch.core/plugin.xml +++ b/bundles/org.eclipse.remote.jsch.core/plugin.xml @@ -44,6 +44,26 @@ factory="org.eclipse.remote.internal.jsch.core.JSchConnection$Factory" service="org.eclipse.remote.internal.jsch.core.JSchConnection"> + + + + + + + + command) { - return new JSchProcessBuilder(this, command); + return new JSchProcessBuilder(getRemoteConnection(), command); } /* @@ -576,7 +576,7 @@ public class JSchConnection implements IRemoteConnectionControlService, IRemoteC */ @Override public IRemoteProcessBuilder getProcessBuilder(String... command) { - return new JSchProcessBuilder(this, command); + return new JSchProcessBuilder(getRemoteConnection(), command); } /* diff --git a/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchConnectionProxyFactory.java b/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchConnectionProxyFactory.java index 0446d5a0d74..4ed92b9d25b 100644 --- a/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchConnectionProxyFactory.java +++ b/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchConnectionProxyFactory.java @@ -24,16 +24,13 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; -import org.eclipse.remote.core.IRemoteConnection; -import org.eclipse.remote.core.IRemoteProcess; import org.eclipse.remote.core.IRemoteProcessService; +import org.eclipse.remote.core.IRemoteProcess; import org.eclipse.remote.core.IRemoteServicesManager; import org.eclipse.remote.core.exception.RemoteConnectionException; import org.eclipse.remote.internal.jsch.core.messages.Messages; import com.jcraft.jsch.Channel; -import com.jcraft.jsch.ChannelExec; -import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Proxy; import com.jcraft.jsch.SocketFactory; @@ -49,12 +46,13 @@ public class JSchConnectionProxyFactory { private String command; private IRemoteProcess process; private JSchConnection connection; - private IProgressMonitor monitor; + private final IProgressMonitor monitor; private boolean connectCalled = false; private CommandProxy(JSchConnection connection, String command, IProgressMonitor monitor) { - if (command == null || monitor == null) + if (command == null || monitor == null) { throw new IllegalArgumentException(); + } this.command = command; this.connection = connection; this.monitor = monitor; @@ -62,7 +60,7 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#close() */ @Override @@ -72,12 +70,11 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#connect(com.jcraft.jsch.SocketFactory, java.lang.String, int, int) */ @Override - public void connect(SocketFactory socket_factory, String host, - int port, int timeout) throws IOException { + public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws IOException { assert !connectCalled : "connect should only be called once"; //$NON-NLS-1$ try { if (timeout == 0) { @@ -88,11 +85,14 @@ public class JSchConnectionProxyFactory { SubMonitor subMon = SubMonitor.convert(monitor, waitSteps * 2); final SubMonitor childMon = subMon.newChild(waitSteps); + if (connection == null) { + IRemoteServicesManager manager = Activator.getService(IRemoteServicesManager.class); + connection = (JSchConnection) manager.getLocalConnectionType().getConnections().get(0); + } + // Open connection if it isn't already opened try { - if (connection != null) { - connection.openMinimal(childMon); - } + connection.openMinimal(childMon); } catch (RemoteConnectionException e) { throw new IOException(e); } @@ -102,22 +102,11 @@ public class JSchConnectionProxyFactory { command = command.replace("%h", host); //$NON-NLS-1$ command = command.replace("%p", Integer.toString(port)); //$NON-NLS-1$ - if (connection != null) { - // The process-builder adds unnecessary extra commands (cd, export, ..) and might not work for restricted shells - try { - ChannelExec exec = connection.getExecChannel(); - exec.setCommand(command); - exec.connect(); - process = new JSchProcess(exec, false); - } catch (JSchException | RemoteConnectionException e) { - throw new IOException(e); - } - } else { - List cmd = new ArgumentParser(command).getTokenList(); - IRemoteServicesManager manager = Activator.getService(IRemoteServicesManager.class); - IRemoteConnection connection = manager.getLocalConnectionType().getConnections().get(0); - process = connection.getService(IRemoteProcessService.class).getProcessBuilder(cmd).start(); - } + List cmd = new ArgumentParser(command).getTokenList(); + JSchProcessBuilder processBuilder = (JSchProcessBuilder) connection.getRemoteConnection() + .getService(IRemoteProcessService.class).getProcessBuilder(cmd); + processBuilder.setPreamble(false); + process = processBuilder.start(); // Wait on command to produce stdout output long endTime = System.currentTimeMillis() + timeout; @@ -154,8 +143,8 @@ public class JSchConnectionProxyFactory { } else if (bCanceled) { cause = Messages.JSchConnectionProxyFactory_wasCanceled; } - throw new IOException(MessageFormat.format(Messages.JSchConnectionProxyFactory_ProxyCommandFailed, - command, cause, msg)); + throw new IOException(MessageFormat.format(Messages.JSchConnectionProxyFactory_ProxyCommandFailed, command, + cause, msg)); } // Dump the stderr to log @@ -166,8 +155,7 @@ public class JSchConnectionProxyFactory { String line; try { while ((line = bufferedReader.readLine()) != null) { - log.log(new Status(IStatus.INFO, Activator.getUniqueIdentifier(), - IStatus.OK, line, null)); + log.log(new Status(IStatus.INFO, Activator.getUniqueIdentifier(), IStatus.OK, line, null)); } } catch (IOException e) { Activator.log(e); @@ -181,7 +169,7 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#getInputStream() */ @Override @@ -191,7 +179,7 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#getOutputStream() */ @Override @@ -201,7 +189,7 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#getSocket() */ @Override @@ -212,20 +200,21 @@ public class JSchConnectionProxyFactory { private static class SSHForwardProxy implements Proxy { private Channel channel; - private JSchConnection connection; - private IProgressMonitor monitor; + private final JSchConnection connection; + private final IProgressMonitor monitor; private boolean connectCalled = false; private SSHForwardProxy(JSchConnection proxyConnection, IProgressMonitor monitor) { - if (proxyConnection == null || monitor == null) + if (proxyConnection == null || monitor == null) { throw new IllegalArgumentException(); + } this.connection = proxyConnection; this.monitor = monitor; } /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#close() */ @Override @@ -235,12 +224,11 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#connect(com.jcraft.jsch.SocketFactory, java.lang.String, int, int) */ @Override - public void connect(SocketFactory socket_factory, String host, int port, - int timeout) throws Exception { + public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws Exception { assert !connectCalled : "connect should only be called once"; //$NON-NLS-1$ try { if (!connection.hasOpenSession()) { @@ -258,7 +246,7 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#getInputStream() */ @Override @@ -273,7 +261,7 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#getOutputStream() */ @Override @@ -288,7 +276,7 @@ public class JSchConnectionProxyFactory { /* * (non-Javadoc) - * + * * @see com.jcraft.jsch.Proxy#getSocket() */ @Override diff --git a/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcess.java b/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcess.java index 5c99c8c9132..7db09870f68 100644 --- a/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcess.java +++ b/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcess.java @@ -16,20 +16,56 @@ import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; -import org.eclipse.remote.core.AbstractRemoteProcess; +import org.eclipse.remote.core.IRemoteProcess; +import org.eclipse.remote.core.IRemoteProcessControlService; +import org.eclipse.remote.core.IRemoteProcessSignalService; +import org.eclipse.remote.core.IRemoteProcessTerminalService; +import org.eclipse.remote.core.exception.RemoteConnectionException; import com.jcraft.jsch.ChannelExec; -public class JSchProcess extends AbstractRemoteProcess { +public class JSchProcess implements IRemoteProcessControlService, IRemoteProcessSignalService, IRemoteProcessTerminalService { + @SuppressWarnings("nls") + private final String signals[] = new String[] { "", "HUP", "INT", "QUIT", "ILL", "", "ABRT", "", "FPE", "KILL", "", "SEGV", "", + "PIPE", "ALRM", "TERM", "", "STOP", "TSTP", "CONT", "", "", "", "", "", "", "", "", "", "", "USR1", "USR2" }; + private static int WAIT_TIMEOUT = 1000; private static int refCount = 0; private final ChannelExec fChannel; + private final IRemoteProcess fProcess; + private InputStream fProcStdout; private InputStream fProcStderr; private Thread fStdoutReader; private Thread fStderrReader; + public static class Factory implements IRemoteProcess.Service.Factory { + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcess.Service.Factory#getService(org.eclipse.remote.core.IRemoteProcess, + * java.lang.Class) + */ + @SuppressWarnings("unchecked") + @Override + public T getService(IRemoteProcess remoteProcess, Class service) { + // This little trick creates an instance of this class + // then for each interface it implements, it returns the same object. + // This works because the connection caches the service so only one gets created. + // As a side effect, it makes this class a service too which can be used + // by the this plug-in + if (JSchProcess.class.equals(service)) { + return (T) new JSchProcess(remoteProcess); + } + if (IRemoteProcessControlService.class.equals(service) || IRemoteProcessSignalService.class.equals(service) + || IRemoteProcessTerminalService.class.equals(service)) { + return (T) remoteProcess.getService(JSchProcess.class); + } + return null; + } + } + private class ProcReader implements Runnable { private final static int BUF_SIZE = 8192; @@ -44,6 +80,7 @@ public class JSchProcess extends AbstractRemoteProcess { } } + @Override public void run() { int len; byte b[] = new byte[BUF_SIZE]; @@ -79,23 +116,29 @@ public class JSchProcess extends AbstractRemoteProcess { } } - public JSchProcess(ChannelExec channel, boolean merge) throws IOException { - fChannel = channel; + public JSchProcess(IRemoteProcess process) { + fProcess = process; + fChannel = ((JSchProcessBuilder) process.getProcessBuilder()).getChannel(); - if (merge) { - PipedOutputStream pipedOutput = new PipedOutputStream(); + try { + if (process.getProcessBuilder().redirectErrorStream()) { + PipedOutputStream pipedOutput = new PipedOutputStream(); - fProcStdout = new PipedInputStream(pipedOutput); - fProcStderr = new NullInputStream(); + fProcStdout = new PipedInputStream(pipedOutput); + fProcStderr = new NullInputStream(); - fStderrReader = new Thread(new ProcReader(channel.getErrStream(), pipedOutput)); - fStdoutReader = new Thread(new ProcReader(channel.getInputStream(), pipedOutput)); + fStderrReader = new Thread(new ProcReader(fChannel.getErrStream(), pipedOutput)); + fStdoutReader = new Thread(new ProcReader(fChannel.getInputStream(), pipedOutput)); - fStderrReader.start(); - fStdoutReader.start(); - } else { - fProcStdout = channel.getInputStream(); - fProcStderr = channel.getErrStream(); + fStderrReader.start(); + fStdoutReader.start(); + } else { + fProcStdout = fChannel.getInputStream(); + fProcStderr = fChannel.getErrStream(); + } + } catch (IOException e) { + Activator.log(e); + destroy(); } } @@ -170,10 +213,46 @@ public class JSchProcess extends AbstractRemoteProcess { /* * (non-Javadoc) * - * @see org.eclipse.remote.core.AbstractRemoteProcess#isCompleted() + * @see org.eclipse.remote.core.RemoteProcess#isCompleted() */ @Override public boolean isCompleted() { return fChannel.isClosed(); } + + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcess.Service#getRemoteProcess() + */ + @Override + public IRemoteProcess getRemoteProcess() { + return fProcess; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcessTerminalService#setTerminalSize(int, int, int, int) + */ + @Override + public void setTerminalSize(int cols, int rows, int width, int height) { + fChannel.setPtySize(cols, rows, width, height); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.remote.core.IRemoteProcessSignalService#sendSignal(int) + */ + @Override + public void sendSignal(int signal) throws RemoteConnectionException { + if (signal >= 0 && signal <= USR2) { + try { + fChannel.sendSignal(signals[signal]); + } catch (Exception e) { + throw new RemoteConnectionException(e.getMessage()); + } + } + } } diff --git a/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcessBuilder.java b/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcessBuilder.java index 5793bd8c301..820400e5a18 100644 --- a/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcessBuilder.java +++ b/bundles/org.eclipse.remote.jsch.core/src/org/eclipse/remote/internal/jsch/core/JSchProcessBuilder.java @@ -24,10 +24,12 @@ import java.util.Set; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.remote.core.AbstractRemoteProcessBuilder; +import org.eclipse.remote.core.IRemoteConnection; import org.eclipse.remote.core.IRemoteFileService; import org.eclipse.remote.core.IRemoteProcess; import org.eclipse.remote.core.exception.RemoteConnectionException; import org.eclipse.remote.internal.core.RemoteDebugOptions; +import org.eclipse.remote.internal.core.RemoteProcess; import org.eclipse.remote.internal.jsch.core.messages.Messages; import com.jcraft.jsch.ChannelExec; @@ -39,11 +41,13 @@ public class JSchProcessBuilder extends AbstractRemoteProcessBuilder { private final Map fRemoteEnv = new HashMap(); private final Set charSet = new HashSet(); - private Map fNewRemoteEnv = null; + private ChannelExec fChannel; + private Map fNewRemoteEnv; + private boolean fPreamble = true; - public JSchProcessBuilder(JSchConnection connection, List command) { - super(command); - fConnection = connection; + public JSchProcessBuilder(IRemoteConnection connection, List command) { + super(connection, command); + fConnection = connection.getService(JSchConnection.class); fRemoteEnv.putAll(fConnection.getEnv()); // Create set of characters not to escape @@ -56,7 +60,7 @@ public class JSchProcessBuilder extends AbstractRemoteProcessBuilder { } } - public JSchProcessBuilder(JSchConnection connection, String... command) { + public JSchProcessBuilder(IRemoteConnection connection, String... command) { this(connection, Arrays.asList(command)); } @@ -151,14 +155,14 @@ public class JSchProcessBuilder extends AbstractRemoteProcessBuilder { } try { - ChannelExec exec = fConnection.getExecChannel(); + fChannel = fConnection.getExecChannel(); String command = buildCommand(remoteCmd, env, clearEnv); - exec.setCommand(command); - exec.setPty((flags & ALLOCATE_PTY) == ALLOCATE_PTY); - exec.setXForwarding((flags & FORWARD_X11) == FORWARD_X11); - exec.connect(); + fChannel.setCommand(command); + fChannel.setPty((flags & ALLOCATE_PTY) == ALLOCATE_PTY); + fChannel.setXForwarding((flags & FORWARD_X11) == FORWARD_X11); + fChannel.connect(); RemoteDebugOptions.trace(RemoteDebugOptions.DEBUG_REMOTE_COMMANDS, "executing command: " + command); //$NON-NLS-1$ - return new JSchProcess(exec, redirectErrorStream()); + return new RemoteProcess(getRemoteConnection(), this); } catch (RemoteConnectionException e) { throw new IOException(e.getMessage()); } catch (JSchException e) { @@ -166,24 +170,34 @@ public class JSchProcessBuilder extends AbstractRemoteProcessBuilder { } } + public ChannelExec getChannel() { + return fChannel; + } + + public void setPreamble(boolean enable) { + fPreamble = enable; + } + private String buildCommand(String cmd, List environment, boolean clearEnv) { StringBuffer sb = new StringBuffer(); - if (directory() != null) { - sb.append("cd " + charEscapify(directory().toURI().getPath(), charSet) + " && "); //$NON-NLS-1$ //$NON-NLS-2$ - } - if (clearEnv) { - sb.append("env -i"); //$NON-NLS-1$ - for (String env : environment) { - sb.append(" \"" + env + "\""); //$NON-NLS-1$ //$NON-NLS-2$ + if (fPreamble) { + if (directory() != null) { + sb.append("cd " + charEscapify(directory().toURI().getPath(), charSet) + " && "); //$NON-NLS-1$ //$NON-NLS-2$ } - sb.append(" "); //$NON-NLS-1$ - } else { - for (String env : environment) { - sb.append("export \"" + env + "\"; "); //$NON-NLS-1$ //$NON-NLS-2$ + if (clearEnv) { + sb.append("env -i"); //$NON-NLS-1$ + for (String env : environment) { + sb.append(" \"" + env + "\""); //$NON-NLS-1$ //$NON-NLS-2$ + } + sb.append(" "); //$NON-NLS-1$ + } else { + for (String env : environment) { + sb.append("export \"" + env + "\"; "); //$NON-NLS-1$ //$NON-NLS-2$ + } } } sb.append(cmd); - if (fConnection.useLoginShell()) { + if (fPreamble && fConnection.useLoginShell()) { sb.insert(0, "/bin/bash -l -c '"); //$NON-NLS-1$ sb.append("'"); //$NON-NLS-1$ }