1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-08 10:16:03 +02:00

Bug 457493 - Add support for window terminal size and signals.

Change-Id: Iceb1786796c43c70584d6e01824ffe56e9bed255
Signed-off-by: Greg Watson <g.watson@computer.org>
This commit is contained in:
Greg Watson 2015-03-06 17:08:34 -05:00
parent fab5645c94
commit d5cb7731db
21 changed files with 804 additions and 230 deletions

View file

@ -33,6 +33,16 @@
factory="org.eclipse.remote.internal.core.services.local.LocalProcessService$Factory"
service="org.eclipse.remote.core.IRemoteProcessService">
</connectionService>
<processService
connectionTypeId="org.eclipse.remote.LocalServices"
factory="org.eclipse.remote.internal.core.services.local.LocalProcess$Factory"
service="org.eclipse.remote.core.IRemoteProcessControlService">
</processService>
<processService
connectionTypeId="org.eclipse.remote.LocalServices"
factory="org.eclipse.remote.internal.core.services.local.LocalProcess$Factory"
service="org.eclipse.remote.internal.core.services.local.LocalProcess">
</processService>
</extension>
<extension
point="org.eclipse.core.runtime.adapters">

View file

@ -21,6 +21,7 @@
<element ref="connectionType" minOccurs="0" maxOccurs="unbounded"/>
<element ref="connectionTypeService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="connectionService" minOccurs="0" maxOccurs="unbounded"/>
<element ref="processService" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="point" type="string" use="required">
<annotation>
@ -173,6 +174,46 @@
</complexType>
</element>
<element name="processService">
<annotation>
<documentation>
This is a service that implements the given service interface for processes of a given connection type.
</documentation>
</annotation>
<complexType>
<attribute name="connectionTypeId" type="string" use="required">
<annotation>
<documentation>
The connection type for the connections that this service applies to.
</documentation>
<appInfo>
<meta.attribute kind="identifier" basedOn="org.eclipse.remote.core.remoteServices/connectionType/@id"/>
</appInfo>
</annotation>
</attribute>
<attribute name="service" type="string" use="required">
<annotation>
<documentation>
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.
</documentation>
<appInfo>
<meta.attribute kind="java"/>
</appInfo>
</annotation>
</attribute>
<attribute name="factory" type="string" use="required">
<annotation>
<documentation>
The factory class used to instantiate the service.
</documentation>
<appInfo>
<meta.attribute kind="java" basedOn=":org.eclipse.remote.core.IRemoteProcess$Service$Factory"/>
</appInfo>
</annotation>
</attribute>
</complexType>
</element>
<annotation>
<appInfo>

View file

@ -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;
}
}

View file

@ -25,15 +25,24 @@ import org.eclipse.core.filesystem.IFileStore;
*/
public abstract class AbstractRemoteProcessBuilder implements IRemoteProcessBuilder {
private List<String> fCommandArgs;
private IFileStore fRemoteDir = null;
private boolean fRedirectErrorStream = false;
private IFileStore fRemoteDir;
private boolean fRedirectErrorStream;
public AbstractRemoteProcessBuilder(List<String> command) {
private final IRemoteConnection fConnection;
/**
* @since 2.0
*/
public AbstractRemoteProcessBuilder(IRemoteConnection connection, List<String> 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;
}
}

View file

@ -110,6 +110,15 @@ public interface IRemoteConnectionType {
*/
<T extends IRemoteConnection.Service> boolean hasConnectionService(Class<T> 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
*/
<T extends IRemoteProcess.Service> boolean hasProcessService(Class<T> service);
/**
* Gets the remote connection corresponding to the supplied name.
*

View file

@ -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 extends Service> T getService(IRemoteProcess remoteProcess, Class<T> 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 extends Service> T getService(Class<T> 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
*/
<T extends Service> boolean hasService(Class<T> 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();
}

View file

@ -46,7 +46,7 @@ public interface IRemoteProcessBuilder {
*
* @return a list containing the program and arguments
*/
public List<String> command();
List<String> 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<String> command);
IRemoteProcessBuilder command(List<String> 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<String, String> environment();
Map<String, String> 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();
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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 extends IRemoteProcess.Service> T getProcessService(IRemoteProcess process, Class<T> 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 <T extends IRemoteProcess.Service> boolean hasProcessService(Class<T> service) {
return serviceDefinitionMap.get(service.getName()) != null;
}
/**
* Called from the remote service manager to register a service extension for
* this remote services implementation

View file

@ -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<String, Object> 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 extends Service> T getService(Class<T> 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 <T extends Service> boolean hasService(Class<T> 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();
}
}

View file

@ -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) {

View file

@ -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 extends IRemoteProcess.Service> T getService(IRemoteProcess remoteProcess, Class<T> 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;
}
}

View file

@ -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<String, String> fRemoteEnv = new HashMap<String, String>();
public LocalProcessBuilder(List<String> command) {
super(command);
private Process localProcess;
public LocalProcessBuilder(IRemoteConnection connection, List<String> 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<String, String> 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() {

View file

@ -46,12 +46,12 @@ public class LocalProcessService implements IRemoteProcessService {
@Override
public IRemoteProcessBuilder getProcessBuilder(List<String> 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

View file

@ -44,6 +44,26 @@
factory="org.eclipse.remote.internal.jsch.core.JSchConnection$Factory"
service="org.eclipse.remote.internal.jsch.core.JSchConnection">
</connectionService>
<processService
connectionTypeId="org.eclipse.remote.JSch"
factory="org.eclipse.remote.internal.jsch.core.JSchProcess$Factory"
service="org.eclipse.remote.core.IRemoteProcessControlService">
</processService>
<processService
connectionTypeId="org.eclipse.remote.JSch"
factory="org.eclipse.remote.internal.jsch.core.JSchProcess$Factory"
service="org.eclipse.remote.core.IRemoteProcessSignalService">
</processService>
<processService
connectionTypeId="org.eclipse.remote.JSch"
factory="org.eclipse.remote.internal.jsch.core.JSchProcess$Factory"
service="org.eclipse.remote.core.IRemoteProcessTerminalService">
</processService>
<processService
connectionTypeId="org.eclipse.remote.JSch"
factory="org.eclipse.remote.internal.jsch.core.JSchProcess$Factory"
service="org.eclipse.remote.internal.jsch.core.JSchProcess">
</processService>
</extension>
<extension
id="org.eclipse.remote.jsch.filesystem"

View file

@ -566,7 +566,7 @@ public class JSchConnection implements IRemoteConnectionControlService, IRemoteC
*/
@Override
public IRemoteProcessBuilder getProcessBuilder(List<String> 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);
}
/*

View file

@ -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<String> 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<String> 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

View file

@ -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 extends IRemoteProcess.Service> T getService(IRemoteProcess remoteProcess, Class<T> 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());
}
}
}
}

View file

@ -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<String, String> fRemoteEnv = new HashMap<String, String>();
private final Set<Character> charSet = new HashSet<Character>();
private Map<String, String> fNewRemoteEnv = null;
private ChannelExec fChannel;
private Map<String, String> fNewRemoteEnv;
private boolean fPreamble = true;
public JSchProcessBuilder(JSchConnection connection, List<String> command) {
super(command);
fConnection = connection;
public JSchProcessBuilder(IRemoteConnection connection, List<String> 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<String> 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$
}