1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-01 06:05:24 +02:00

[170910] Adopt RSE ITerminalService API for SSH

This commit is contained in:
Martin Oberhuber 2008-04-11 17:19:26 +00:00
parent a6dbfe3f23
commit c99db80573
11 changed files with 459 additions and 23 deletions

View file

@ -13,5 +13,6 @@ Bundle-ActivationPolicy: lazy
Eclipse-LazyStart: true
Export-Package: org.eclipse.rse.internal.services.ssh;x-friends:="org.eclipse.rse.connectorservice.ssh,org.eclipse.rse.subsystems.files.ssh,org.eclipse.rse.subsystems.shells.ssh",
org.eclipse.rse.internal.services.ssh.files;x-friends:="org.eclipse.rse.connectorservice.ssh,org.eclipse.rse.subsystems.files.ssh,org.eclipse.rse.subsystems.shells.ssh",
org.eclipse.rse.internal.services.ssh.shell;x-friends:="org.eclipse.rse.connectorservice.ssh,org.eclipse.rse.subsystems.files.ssh,org.eclipse.rse.subsystems.shells.ssh"
org.eclipse.rse.internal.services.ssh.shell;x-friends:="org.eclipse.rse.connectorservice.ssh,org.eclipse.rse.subsystems.files.ssh,org.eclipse.rse.subsystems.shells.ssh",
org.eclipse.rse.internal.services.ssh.terminal;x-internal:=true
Bundle-RequiredExecutionEnvironment: J2SE-1.4

View file

@ -1,24 +1,33 @@
/*******************************************************************************
* Copyright (c) 2006, 2007 Wind River Systems, Inc.
* 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:
* Martin Oberhuber (Wind River) - initial API and implementation
* Copyright (c) 2006, 2008 Wind River Systems, Inc.
* 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:
* Martin Oberhuber (Wind River) - initial API and implementation
* Martin Oberhuber (Wind River) - [170910] Adopt RSE ITerminalService API for SSH
*******************************************************************************/
package org.eclipse.rse.internal.services.ssh;
/**
/**
* Markup Interface for services using the SshConnectorService.
*
* By implementing this interface, services can be recognized
* as operating against an SshConnectorService. The interface
* as operating against an SshConnectorService. The interface
* is used as the key in a table for looking up the connector
* service when needed.
*/
public interface ISshService {
/**
* Get the Session Provider that cares for connect / disconnect of an SSH
* Session, and can be used to instantiate new Channels.
*
* @return an SSH Session Provider.
*/
public ISshSessionProvider getSessionProvider();
}

View file

@ -7,6 +7,7 @@
*
* Contributors:
* Martin Oberhuber (Wind River) - initial API and implementation
* Yu-Fen Kuo (MontaVista) - [170910] Integrate the TM Terminal View with RSE
*******************************************************************************/
package org.eclipse.rse.internal.services.ssh;
@ -33,6 +34,11 @@ public class SshServiceResources extends NLS {
public static String SshShellService_Description;
public static String SshShellService_Name;
public static String SshTerminalService_Name;
public static String SshTerminalService_Description;
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, SshServiceResources.class);

View file

@ -7,6 +7,7 @@
#
# Contributors:
# Martin Oberhuber (Wind River) - initial API and implementation
# Yu-Fen Kuo (MontaVista) - [170910] Integrate the TM Terminal View with RSE
################################################################################
# NLS_MESSAGEFORMAT_VAR
@ -23,3 +24,6 @@ SftpFileService_Msg_Progress={0,number,integer} KB of {1,number,integer} KB comp
SshShellService_Name=SSH Shell Service
SshShellService_Description=SSH Shell Service Description
SshTerminalService_Name=SSH Terminal Service
SshTerminalService_Description=SSH Terminal Service Description

View file

@ -25,6 +25,7 @@
* David McKnight (IBM) - [216252] [api][nls] Resource Strings specific to subsystems should be moved from rse.ui into files.ui / shells.ui / processes.ui where possible
* Martin Oberhuber (Wind River) - [224799] Fix JSch encoding problems with Arabic filenames
* Martin Oberhuber (Wind River) - [226262] Make IService IAdaptable
* Martin Oberhuber (Wind River) - [170910] Adopt RSE ITerminalService API for SSH
*******************************************************************************/
package org.eclipse.rse.internal.services.ssh.files;
@ -154,7 +155,7 @@ public class SftpFileService extends AbstractFileService implements ISshService,
}
//private SshConnectorService fConnector;
private ISshSessionProvider fSessionProvider;
private final ISshSessionProvider fSessionProvider;
private ChannelSftp fChannelSftp;
private String fUserHome;
private Mutex fDirChannelMutex = new Mutex();
@ -174,6 +175,10 @@ public class SftpFileService extends AbstractFileService implements ISshService,
fSessionProvider = sessionProvider;
}
public ISshSessionProvider getSessionProvider() {
return fSessionProvider;
}
public void setControlEncoding(String encoding) throws SystemMessageException {
try {
fChannelSftp.setFilenameEncoding(encoding);

View file

@ -16,15 +16,19 @@
* Martin Oberhuber (Wind River) - [186128] Move IProgressMonitor last in all API
* Martin Oberhuber (Wind River) - [226262] Make IService IAdaptable
* Martin Oberhuber (Wind River) - [226301][api] IShellService should throw SystemMessageException on error
* Martin Oberhuber (Wind River) - [170910] Adopt RSE ITerminalService API for SSH
*******************************************************************************/
package org.eclipse.rse.internal.services.ssh.shell;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.rse.internal.services.ssh.ISshService;
import org.eclipse.rse.internal.services.ssh.ISshSessionProvider;
import org.eclipse.rse.internal.services.ssh.SshServiceResources;
import org.eclipse.rse.internal.services.ssh.terminal.SshTerminalService;
import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
import org.eclipse.rse.services.shells.AbstractShellService;
import org.eclipse.rse.services.shells.IHostShell;
@ -35,7 +39,8 @@ import org.eclipse.rse.services.shells.IHostShell;
*/
public class SshShellService extends AbstractShellService implements ISshService {
private ISshSessionProvider fSessionProvider;
private final ISshSessionProvider fSessionProvider;
private SshTerminalService fRelatedTerminalService;
public SshShellService(ISshSessionProvider sessionProvider) {
fSessionProvider = sessionProvider;
@ -55,6 +60,45 @@ public class SshShellService extends AbstractShellService implements ISshService
return hostShell;
}
/**
* Return an RSE ITerminalService related to this Shell Service.
*/
protected synchronized SshTerminalService getRelatedTerminalService() {
if (fRelatedTerminalService == null) {
fRelatedTerminalService = new SshTerminalService(getSessionProvider());
}
return fRelatedTerminalService;
}
/**
* Adapt this shell service to different (potentially contributed)
* interfaces.
*
* Asks the adapter manager first whether it got any contributed adapter; if
* none is found contributed externally, try to adapt to an
* SshTerminalService. That way, clients can easily convert this
* IShellService into an ITerminalService:
*
* <pre>
* ITerminalService ts = (ITerminalService) myShellService.getAdapter(ITerminalService.class);
* </pre>
*
* @see IAdaptable
* @see PlatformObject#getAdapter(Class)
*/
public Object getAdapter(Class adapter) {
// TODO I'm not sure if this is the right way doing things. First of
// all, we're holding on to the created terminal service forever if
// we're asked for it, thus needing extra memory.
// Second, by asking the adapter manager first, we might get no chance
// returning what we think is right.
Object o = super.getAdapter(adapter);
if (o == null && adapter.isAssignableFrom(SshTerminalService.class)) {
return getRelatedTerminalService();
}
return o;
}
public String getName() {
return SshServiceResources.SshShellService_Name;
}
@ -63,4 +107,8 @@ public class SshShellService extends AbstractShellService implements ISshService
return SshServiceResources.SshShellService_Description;
}
public ISshSessionProvider getSessionProvider() {
return fSessionProvider;
}
}

View file

@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc.
* 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:
* Martin Oberhuber (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.rse.internal.services.ssh.terminal;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.rse.internal.services.ssh.ISshService;
import org.eclipse.rse.internal.services.ssh.ISshSessionProvider;
import org.eclipse.rse.internal.services.ssh.SshServiceResources;
import org.eclipse.rse.internal.services.ssh.shell.SshShellService;
import org.eclipse.rse.internal.services.terminals.AbstractTerminalService;
import org.eclipse.rse.internal.services.terminals.ITerminalShell;
import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
/**
* A Terminal Service for ssh.
*/
public class SshTerminalService extends AbstractTerminalService implements ISshService {
private final ISshSessionProvider fSessionProvider;
private SshShellService fRelatedShellService;
public SshTerminalService(ISshSessionProvider sessionProvider) {
fSessionProvider = sessionProvider;
}
public ISshSessionProvider getSessionProvider() {
return fSessionProvider;
}
public ITerminalShell launchTerminal(String ptyType, String encoding, String[] environment, String initialWorkingDirectory, String commandToRun,
IProgressMonitor monitor) throws SystemMessageException {
SshTerminalShell hostShell = new SshTerminalShell(getSessionProvider(), ptyType, encoding, environment, initialWorkingDirectory, commandToRun);
return hostShell;
}
/**
* Return an RSE IShellService related to this Terminal Service.
*/
protected synchronized SshShellService getRelatedShellService() {
if (fRelatedShellService == null) {
fRelatedShellService = new SshShellService(getSessionProvider());
}
return fRelatedShellService;
}
/**
* Adapt this terminal service to different (potentially contributed)
* interfaces, in order to provide additional functionality.
*
* Asks the adapter manager first whether it got any contributed adapter; if
* none is found contributed externally, try to adapt to an SshShellService.
* That way, clients can easily convert this ITerminalService into an
* IShellService:
*
* <pre>
* IShellService ss = (IShellService) myTerminalService.getAdapter(IShellService.class);
* </pre>
*
* @see IAdaptable
* @see PlatformObject#getAdapter(Class)
*/
public Object getAdapter(Class adapter) {
// TODO I'm not sure if this is the right way doing things. First of
// all, we're holding on to the created terminal service forever if
// we're asked for it, thus needing extra memory.
// Second, by asking the adapter manager first, we might get no chance
// returning what we think is right.
Object o = super.getAdapter(adapter);
if (o==null && adapter.isAssignableFrom(SshShellService.class)) {
return getRelatedShellService();
}
return o;
}
public String getName() {
return SshServiceResources.SshTerminalService_Name;
}
public String getDescription() {
return SshServiceResources.SshTerminalService_Description;
}
}

View file

@ -0,0 +1,229 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc. 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:
* Martin Oberhuber (Wind River) - initial API and implementation
* Anna Dushistova (MontaVista) - [170910] Integrate the TM Terminal View with RSE
*******************************************************************************/
package org.eclipse.rse.internal.services.ssh.terminal;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Hashtable;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.Session;
import org.eclipse.rse.internal.services.ssh.ISshSessionProvider;
import org.eclipse.rse.internal.services.terminals.AbstractTerminalShell;
import org.eclipse.rse.internal.services.terminals.ITerminalService;
import org.eclipse.rse.services.clientserver.PathUtility;
import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
import org.eclipse.rse.services.files.RemoteFileException;
/**
* A remote shell connection supporting Streams for I/O.
*/
public class SshTerminalShell extends AbstractTerminalShell {
private ISshSessionProvider fSessionProvider;
private Channel fChannel;
private String fEncoding;
private InputStream fInputStream;
private OutputStream fOutputStream;
private Writer fOutputStreamWriter;
private int fWidth = 0;
private int fHeight = 0;
private static String defaultEncoding = new java.io.InputStreamReader(new java.io.ByteArrayInputStream(new byte[0])).getEncoding();
/**
* Construct a new Terminal connection.
*
* The SSH channel is immediately connected in the Constructor.
*
* @param sessionProvider SSH session provider
* @param ptyType Terminal type to set, or <code>null</code> if not
* relevant
* @param encoding The default encoding to use for initial command.
* @param environment Environment array to set, or <code>null</code> if
* not relevant.
* @param initialWorkingDirectory initial directory to open the Terminal in.
* Use <code>null</code> or empty String ("") to start in a
* default directory. Empty String will typically start in the
* home directory.
* @param commandToRun initial command to send.
* @throws SystemMessageException in case anything goes wrong. Channels and
* Streams are all cleaned up again in this case.
* @see ITerminalService
*/
public SshTerminalShell(ISshSessionProvider sessionProvider, String ptyType, String encoding, String[] environment, String initialWorkingDirectory,
String commandToRun) throws SystemMessageException {
try {
fSessionProvider = sessionProvider;
fEncoding = encoding;
fChannel = fSessionProvider.getSession().openChannel("shell"); //$NON-NLS-1$
if (ptyType != null && (fChannel instanceof ChannelShell)) {
//By default, jsch always creates a vt100 connection sized
//80x24 / 640x480 (dimensions can be changed).
((ChannelShell) fChannel).setPtyType(ptyType);
}
//Try to set the user environment. On most sshd configurations, this will
//not work since in sshd_config, PermitUserEnvironment and AcceptEnv
//settings are disabled. Still, it's worth a try.
if (environment!=null && environment.length>0 && fChannel instanceof ChannelShell) {
Hashtable envTable=new Hashtable();
for(int i=0; i<environment.length; i++) {
String curStr=environment[i];
int curLen=environment[i].length();
int idx = curStr.indexOf('=');
if (idx>0 && idx<curLen-1) {
String key=environment[i].substring(0, idx);
String value=environment[i].substring(idx+1, curLen);
if (fEncoding != null) {
key = recode(key, fEncoding);
value = recode(value, fEncoding);
}
envTable.put(key, value);
}
}
((ChannelShell) fChannel).setEnv(envTable);
}
fOutputStream = fChannel.getOutputStream();
fInputStream = fChannel.getInputStream();
fChannel.connect();
if (fEncoding != null) {
fOutputStreamWriter = new BufferedWriter(new OutputStreamWriter(fOutputStream, encoding));
} else {
// default encoding == System.getProperty("file.encoding")
// TODO should try to determine remote encoding if possible
fOutputStreamWriter = new BufferedWriter(new OutputStreamWriter(fOutputStream));
}
if (initialWorkingDirectory!=null && initialWorkingDirectory.length()>0
&& !initialWorkingDirectory.equals(".") //$NON-NLS-1$
&& !initialWorkingDirectory.equals("Command Shell") //$NON-NLS-1$ //FIXME workaround for bug 153047
) {
writeToShell("cd " + PathUtility.enQuoteUnix(initialWorkingDirectory)); //$NON-NLS-1$
}
if (commandToRun != null && commandToRun.length() > 0) {
writeToShell(commandToRun);
}
} catch(Exception e) {
throw new RemoteFileException("Error creating Terminal", e); //$NON-NLS-1$
} finally {
isActive();
}
}
public String getDefaultEncoding() {
return fEncoding;
}
/**
* Encode String with requested user encoding, in case it differs from
* Platform default encoding.
*
* @param s String to encode
* @param encoding Encoding to use
* @return encoded String
* @throws UnsupportedEncodingException in case the requested encoding is
* not supported
*/
protected String recode(String s, String encoding) throws UnsupportedEncodingException {
if (encoding == null) {
return s;
} else if (encoding.equals(defaultEncoding)) {
return s;
}
// what we want on the wire:
byte[] bytes = s.getBytes(encoding);
// what we need to tell Jsch to get this on the wire:
return new String(bytes, defaultEncoding);
}
/*
* (non-Javadoc)
* @see ITerminalHostShell#getInputStream(Object)
*/
public InputStream getInputStream() {
return fInputStream;
}
/*
* (non-Javadoc)
* @see ITerminalHostShell#getOutputStream(Object)
*/
public OutputStream getOutputStream() {
return fOutputStream;
}
/**
* Write a command to the shell, honoring specified Encoding. Can only be
* done before an outputStream is obtained, since these commands would
* interfere with the outputStream.
*
* @param command Command String to send, or "#break" to send a Ctrl+C
* command.
*/
public void writeToShell(String command) throws IOException {
if (isActive()) {
if ("#break".equals(command)) { //$NON-NLS-1$
command = "\u0003"; // Unicode 3 == Ctrl+C //$NON-NLS-1$
} else {
command += "\r\n"; //$NON-NLS-1$
}
fOutputStreamWriter.write(command);
fOutputStreamWriter.flush();
}
}
public void exit() {
if (fChannel != null) {
fChannel.disconnect();
isActive();
}
}
public boolean isActive() {
if (fChannel != null && !fChannel.isEOF()) {
return true;
}
// shell is not active: check for session lost
exit();
Session session = fSessionProvider.getSession();
if (session != null && !session.isConnected()) {
fSessionProvider.handleSessionLost();
}
return false;
}
public boolean isLocalEcho() {
return false;
}
public void setTerminalSize(int newWidth, int newHeight) {
if (fChannel != null && fChannel instanceof ChannelShell && (newWidth != fWidth || newHeight != fHeight)) {
// avoid excessive communications due to change size requests by
// caching previous size
ChannelShell channelShell = (ChannelShell) fChannel;
channelShell.setPtySize(newWidth, newHeight, 8 * newWidth, 8 * newHeight);
fWidth = newWidth;
fHeight = newHeight;
}
}
}

View file

@ -13,7 +13,7 @@ Export-Package: org.eclipse.rse.internal.services;x-internal:=true,
org.eclipse.rse.internal.services.clientserver.archiveutils;x-internal:=true,
org.eclipse.rse.internal.services.clientserver.java;x-internal:=true,
org.eclipse.rse.internal.services.shells;x-internal:=true,
org.eclipse.rse.internal.services.terminals;x-internal:=true,
org.eclipse.rse.internal.services.terminals,
org.eclipse.rse.services,
org.eclipse.rse.services.clientserver,
org.eclipse.rse.services.clientserver.archiveutils,

View file

@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2008 Wind River Systems, Inc. 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:
* Martin Oberhuber (Wind River) - initial API and implementation
*******************************************************************************/
package org.eclipse.rse.internal.services.terminals;
import org.eclipse.rse.services.AbstractService;
/**
* Abstract base class for clients to create an ITerminalService.
*
* Currently the main value of this class is to protect extenders from API
* breakage as the ITerminalService is evolved in the future. We might, for
* instance, be adding state of the terminal service or capabilities for getting
* environment variables.
*
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as
* part of a work in progress. There is no guarantee that this API will work or
* that it will remain the same. Please do not use this API without consulting
* with the <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
* @see ITerminalService
* @since org.eclipse.rse.services 3.0
*/
public abstract class AbstractTerminalService extends AbstractService implements ITerminalService {
// empty for now, extenders need to implement the launchTerminal() method
// themselves
}

View file

@ -13,29 +13,28 @@ package org.eclipse.rse.internal.services.terminals;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.rse.services.AbstractService;
import org.eclipse.rse.services.IService;
import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
/**
* Interface for getting Terminal Connections from a remote side, also known as
* terminal session with Streams.
*
*
* One ITerminalService instance is typically associated with one particular
* connection to a (potentially remote) system, such that the ITerminalService
* instance can also hold state data about that session, such as connected
* state, login and authentication information, configuration data or
* environment variables.
*
*
* Each
* {@link #launchTerminal(String, String, String[], String, String, IProgressMonitor)}
* invocation, however, acts as a factory method such that it creates a new
* (remote) process and associated {@link ITerminalShell} connection.
*
*
* @noimplement This interface is not intended to be implemented by clients.
* Clients must subclass the {@link AbstractService} class rather
* than implementing this interface directly.
*
* Clients must subclass the {@link AbstractTerminalService} class
* rather than implementing this interface directly.
*
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as
* part of a work in progress. There is no guarantee that this API will work or
@ -43,9 +42,9 @@ import org.eclipse.rse.services.clientserver.messages.SystemMessageException;
* with the <a href="http://www.eclipse.org/dsdp/tm/">Target Management</a>
* team.
* </p>
*
*
* @since org.eclipse.rse.services 3.0
*
*
*/
public interface ITerminalService extends IService {