mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 14:42:11 +02:00
bug 143417 - Fix notifications for lost ssh sessions
This commit is contained in:
parent
a8614a0cd3
commit
49c9ea405a
6 changed files with 332 additions and 31 deletions
|
@ -14,6 +14,7 @@ package org.eclipse.rse.connectorservice.ssh;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
@ -22,17 +23,32 @@ import org.eclipse.core.runtime.NullProgressMonitor;
|
||||||
import org.eclipse.core.runtime.OperationCanceledException;
|
import org.eclipse.core.runtime.OperationCanceledException;
|
||||||
import org.eclipse.core.runtime.Platform;
|
import org.eclipse.core.runtime.Platform;
|
||||||
import org.eclipse.jface.dialogs.MessageDialog;
|
import org.eclipse.jface.dialogs.MessageDialog;
|
||||||
|
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
|
||||||
|
import org.eclipse.jface.operation.IRunnableContext;
|
||||||
|
import org.eclipse.jface.operation.IRunnableWithProgress;
|
||||||
import org.eclipse.jface.preference.IPreferenceStore;
|
import org.eclipse.jface.preference.IPreferenceStore;
|
||||||
|
import org.eclipse.rse.core.SystemBasePlugin;
|
||||||
import org.eclipse.rse.core.subsystems.AbstractConnectorService;
|
import org.eclipse.rse.core.subsystems.AbstractConnectorService;
|
||||||
|
import org.eclipse.rse.core.subsystems.CommunicationsEvent;
|
||||||
|
import org.eclipse.rse.core.subsystems.IConnectorService;
|
||||||
|
import org.eclipse.rse.core.subsystems.SubSystemConfiguration;
|
||||||
import org.eclipse.rse.model.IHost;
|
import org.eclipse.rse.model.IHost;
|
||||||
|
import org.eclipse.rse.model.ISystemRegistry;
|
||||||
|
import org.eclipse.rse.services.clientserver.messages.SystemMessage;
|
||||||
import org.eclipse.rse.services.ssh.ISshSessionProvider;
|
import org.eclipse.rse.services.ssh.ISshSessionProvider;
|
||||||
|
import org.eclipse.rse.ui.ISystemMessages;
|
||||||
|
import org.eclipse.rse.ui.RSEUIPlugin;
|
||||||
|
import org.eclipse.rse.ui.messages.SystemMessageDialog;
|
||||||
import org.eclipse.swt.widgets.Display;
|
import org.eclipse.swt.widgets.Display;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
|
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
|
||||||
import org.eclipse.team.internal.ccvs.core.util.Util;
|
import org.eclipse.team.internal.ccvs.core.util.Util;
|
||||||
import org.eclipse.team.internal.ccvs.ssh2.CVSSSH2Plugin;
|
import org.eclipse.team.internal.ccvs.ssh2.CVSSSH2Plugin;
|
||||||
import org.eclipse.team.internal.ccvs.ssh2.ISSHContants;
|
import org.eclipse.team.internal.ccvs.ssh2.ISSHContants;
|
||||||
import org.eclipse.team.internal.ccvs.ui.KeyboardInteractiveDialog;
|
import org.eclipse.team.internal.ccvs.ui.KeyboardInteractiveDialog;
|
||||||
import org.eclipse.team.internal.ccvs.ui.UserValidationDialog;
|
import org.eclipse.team.internal.ccvs.ui.UserValidationDialog;
|
||||||
|
import org.eclipse.ui.IWorkbenchWindow;
|
||||||
|
import org.eclipse.ui.PlatformUI;
|
||||||
|
|
||||||
import com.jcraft.jsch.JSch;
|
import com.jcraft.jsch.JSch;
|
||||||
import com.jcraft.jsch.JSchException;
|
import com.jcraft.jsch.JSchException;
|
||||||
|
@ -52,6 +68,7 @@ public class SshConnectorService extends AbstractConnectorService implements ISs
|
||||||
private static final int SSH_DEFAULT_PORT = 22;
|
private static final int SSH_DEFAULT_PORT = 22;
|
||||||
private static JSch jsch=new JSch();
|
private static JSch jsch=new JSch();
|
||||||
private Session session;
|
private Session session;
|
||||||
|
private SessionLostHandler fSessionLostHandler;
|
||||||
|
|
||||||
public SshConnectorService(IHost host) {
|
public SshConnectorService(IHost host) {
|
||||||
//TODO the port parameter doesnt really make sense here since
|
//TODO the port parameter doesnt really make sense here since
|
||||||
|
@ -59,6 +76,7 @@ public class SshConnectorService extends AbstractConnectorService implements ISs
|
||||||
//setPort() on our base class -- I assume the port is meant to
|
//setPort() on our base class -- I assume the port is meant to
|
||||||
//be a local port.
|
//be a local port.
|
||||||
super("SSH Connector Service", "SSH Connector Service Description", host, 0);
|
super("SSH Connector Service", "SSH Connector Service Description", host, 0);
|
||||||
|
fSessionLostHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
@ -243,35 +261,61 @@ public class SshConnectorService extends AbstractConnectorService implements ISs
|
||||||
session.setTimeout(getSshTimeoutInMillis());
|
session.setTimeout(getSshTimeoutInMillis());
|
||||||
String password = getPasswordInformation().getPassword();
|
String password = getPasswordInformation().getPassword();
|
||||||
session.setPassword(password);
|
session.setPassword(password);
|
||||||
MyUserInfo ui = new MyUserInfo(user, password);
|
MyUserInfo userInfo = new MyUserInfo(user, password);
|
||||||
session.setUserInfo(ui);
|
session.setUserInfo(userInfo);
|
||||||
session.setSocketFactory(new ResponsiveSocketFacory(monitor));
|
session.setSocketFactory(new ResponsiveSocketFacory(monitor));
|
||||||
|
|
||||||
//java.util.Hashtable config=new java.util.Hashtable();
|
//java.util.Hashtable config=new java.util.Hashtable();
|
||||||
//config.put("StrictHostKeyChecking", "no");
|
//config.put("StrictHostKeyChecking", "no");
|
||||||
//session.setConfig(config);
|
//session.setConfig(config);
|
||||||
ui.aboutToConnect();
|
userInfo.aboutToConnect();
|
||||||
try {
|
try {
|
||||||
Activator.trace("connecting..."); //$NON-NLS-1$
|
Activator.trace("SshConnectorService.connecting..."); //$NON-NLS-1$
|
||||||
session.connect();
|
session.connect();
|
||||||
Activator.trace("connected"); //$NON-NLS-1$
|
Activator.trace("SshConnectorService.connected"); //$NON-NLS-1$
|
||||||
} catch (JSchException e) {
|
} catch (JSchException e) {
|
||||||
Activator.trace("connect failed: "+e.toString()); //$NON-NLS-1$
|
Activator.trace("SshConnectorService.connect failed: "+e.toString()); //$NON-NLS-1$
|
||||||
if (session.isConnected())
|
if (session.isConnected())
|
||||||
session.disconnect();
|
session.disconnect();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
ui.connectionMade();
|
userInfo.connectionMade();
|
||||||
|
fSessionLostHandler = new SessionLostHandler(this);
|
||||||
|
notifyConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void internalDisconnect(IProgressMonitor monitor)
|
public void internalDisconnect(IProgressMonitor monitor) throws Exception
|
||||||
{
|
{
|
||||||
//TODO: Check, Is disconnect being called because the network (connection) went down?
|
//TODO Will services like the sftp service be disconnected too? Or notified?
|
||||||
//TODO: Fire communication event (aboutToDisconnect) -- see DStoreConnectorService.internalDisconnect()
|
Activator.trace("SshConnectorService.disconnect"); //$NON-NLS-1$
|
||||||
//TODO: Wrap exception in an InvocationTargetException -- see DStoreConnectorService.internalDisconnect()
|
try
|
||||||
//Will services like the sftp service be disconnected too? Or notified?
|
{
|
||||||
Activator.trace("disconnect"); //$NON-NLS-1$
|
if (session != null) {
|
||||||
session.disconnect();
|
// Is disconnect being called because the network (connection) went down?
|
||||||
|
if (fSessionLostHandler != null && fSessionLostHandler.isSessionLost()) {
|
||||||
|
notifyError();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Fire comm event to signal state about to change
|
||||||
|
fireCommunicationsEvent(CommunicationsEvent.BEFORE_DISCONNECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.disconnect();
|
||||||
|
|
||||||
|
// Fire comm event to signal state changed
|
||||||
|
notifyDisconnection();
|
||||||
|
//TODO MOB - keep the session to avoid NPEs in services (disables gc for the session!)
|
||||||
|
// session = null;
|
||||||
|
fSessionLostHandler = null;
|
||||||
|
// DKM - no need to clear uid cache
|
||||||
|
clearPasswordCache(false); // clear in-memory password
|
||||||
|
//clearUserIdCache(); // Clear any cached local user IDs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception exc)
|
||||||
|
{
|
||||||
|
throw new java.lang.reflect.InvocationTargetException(exc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO avoid having jsch type "Session" in the API.
|
//TODO avoid having jsch type "Session" in the API.
|
||||||
|
@ -282,7 +326,206 @@ public class SshConnectorService extends AbstractConnectorService implements ISs
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Display getStandardDisplay() {
|
/**
|
||||||
|
* Handle session-lost events.
|
||||||
|
* This is generic for any sort of connector service.
|
||||||
|
* Most of this is extracted from dstore's ConnectionStatusListener.
|
||||||
|
*
|
||||||
|
* TODO should be refactored to make it generally available, and allow
|
||||||
|
* dstore to derive from it.
|
||||||
|
*/
|
||||||
|
public static class SessionLostHandler implements Runnable, IRunnableWithProgress
|
||||||
|
{
|
||||||
|
private IConnectorService _connection;
|
||||||
|
private boolean fSessionLost;
|
||||||
|
|
||||||
|
public SessionLostHandler(IConnectorService cs)
|
||||||
|
{
|
||||||
|
_connection = cs;
|
||||||
|
fSessionLost = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify that the connection has been lost. This may be called
|
||||||
|
* multiple times from multiple subsystems. The SessionLostHandler
|
||||||
|
* ensures that actual user feedback and disconnect actions are
|
||||||
|
* done only once, on the first invocation.
|
||||||
|
*/
|
||||||
|
public void sessionLost()
|
||||||
|
{
|
||||||
|
//avoid duplicate execution of sessionLost
|
||||||
|
boolean showSessionLostDlg=false;
|
||||||
|
synchronized(this) {
|
||||||
|
if (!fSessionLost) {
|
||||||
|
fSessionLost = true;
|
||||||
|
showSessionLostDlg=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (showSessionLostDlg) {
|
||||||
|
Display.getDefault().asyncExec(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isSessionLost() {
|
||||||
|
return fSessionLost;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
Shell shell = getShell();
|
||||||
|
//TODO need a more correct message for "session lost"
|
||||||
|
//TODO allow users to reconnect from this dialog
|
||||||
|
//SystemMessage msg = RSEUIPlugin.getPluginMessage(ISystemMessages.MSG_CONNECT_UNKNOWNHOST);
|
||||||
|
SystemMessage msg = RSEUIPlugin.getPluginMessage(ISystemMessages.MSG_CONNECT_CANCELLED);
|
||||||
|
msg.makeSubstitution(_connection.getPrimarySubSystem().getHost().getAliasName());
|
||||||
|
SystemMessageDialog dialog = new SystemMessageDialog(getShell(), msg);
|
||||||
|
dialog.open();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IRunnableContext runnableContext = getRunnableContext(getShell());
|
||||||
|
// will do this.run(IProgressMonitor mon)
|
||||||
|
runnableContext.run(false,true,this); // inthread, cancellable, IRunnableWithProgress
|
||||||
|
_connection.reset();
|
||||||
|
ISystemRegistry sr = RSEUIPlugin.getDefault().getSystemRegistry();
|
||||||
|
sr.connectedStatusChange(_connection.getPrimarySubSystem(), false, true, true);
|
||||||
|
}
|
||||||
|
catch (InterruptedException exc) // user cancelled
|
||||||
|
{
|
||||||
|
if (shell != null)
|
||||||
|
showDisconnectCancelledMessage(shell, _connection.getHostName(), _connection.getPort());
|
||||||
|
}
|
||||||
|
catch (java.lang.reflect.InvocationTargetException invokeExc) // unexpected error
|
||||||
|
{
|
||||||
|
Exception exc = (Exception)invokeExc.getTargetException();
|
||||||
|
if (shell != null)
|
||||||
|
showDisconnectErrorMessage(shell, _connection.getHostName(), _connection.getPort(), exc);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
SystemBasePlugin.logError("ConnectionStatusListener: Error disconnecting", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(IProgressMonitor monitor)
|
||||||
|
throws InvocationTargetException, InterruptedException
|
||||||
|
{
|
||||||
|
String message = null;
|
||||||
|
message = SubSystemConfiguration.getDisconnectingMessage(
|
||||||
|
_connection.getHostName(), _connection.getPort());
|
||||||
|
monitor.beginTask(message, IProgressMonitor.UNKNOWN);
|
||||||
|
try {
|
||||||
|
_connection.disconnect(monitor);
|
||||||
|
} catch (Exception exc) {
|
||||||
|
if (exc instanceof java.lang.reflect.InvocationTargetException)
|
||||||
|
throw (java.lang.reflect.InvocationTargetException) exc;
|
||||||
|
if (exc instanceof java.lang.InterruptedException)
|
||||||
|
throw (java.lang.InterruptedException) exc;
|
||||||
|
throw new java.lang.reflect.InvocationTargetException(exc);
|
||||||
|
} finally {
|
||||||
|
monitor.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Shell getShell() {
|
||||||
|
Shell activeShell = SystemBasePlugin.getActiveWorkbenchShell();
|
||||||
|
if (activeShell != null) {
|
||||||
|
return activeShell;
|
||||||
|
}
|
||||||
|
|
||||||
|
IWorkbenchWindow window = null;
|
||||||
|
try {
|
||||||
|
window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (window == null) {
|
||||||
|
IWorkbenchWindow[] windows = PlatformUI.getWorkbench()
|
||||||
|
.getWorkbenchWindows();
|
||||||
|
if (windows != null && windows.length > 0) {
|
||||||
|
return windows[0].getShell();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return window.getShell();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the progress monitor dialog for this operation. We try to use one
|
||||||
|
* for all phases of a single operation, such as connecting and
|
||||||
|
* resolving.
|
||||||
|
*/
|
||||||
|
protected IRunnableContext getRunnableContext(Shell rshell) {
|
||||||
|
Shell shell = getShell();
|
||||||
|
// for other cases, use statusbar
|
||||||
|
IWorkbenchWindow win = SystemBasePlugin.getActiveWorkbenchWindow();
|
||||||
|
if (win != null) {
|
||||||
|
Shell winShell = RSEUIPlugin.getDefault().getWorkbench()
|
||||||
|
.getActiveWorkbenchWindow().getShell();
|
||||||
|
if (winShell != null && !winShell.isDisposed()
|
||||||
|
&& winShell.isVisible()) {
|
||||||
|
SystemBasePlugin
|
||||||
|
.logInfo("Using active workbench window as runnable context");
|
||||||
|
shell = winShell;
|
||||||
|
return win;
|
||||||
|
} else {
|
||||||
|
win = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shell == null || shell.isDisposed() || !shell.isVisible()) {
|
||||||
|
SystemBasePlugin
|
||||||
|
.logInfo("Using progress monitor dialog with given shell as parent");
|
||||||
|
shell = rshell;
|
||||||
|
}
|
||||||
|
IRunnableContext dlg = new ProgressMonitorDialog(rshell);
|
||||||
|
return dlg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show an error message when the disconnection fails. Shows a common
|
||||||
|
* message by default. Overridable.
|
||||||
|
*/
|
||||||
|
protected void showDisconnectErrorMessage(Shell shell, String hostName, int port, Exception exc)
|
||||||
|
{
|
||||||
|
//SystemMessage.displayMessage(SystemMessage.MSGTYPE_ERROR,shell,RSEUIPlugin.getResourceBundle(),
|
||||||
|
// ISystemMessages.MSG_DISCONNECT_FAILED,
|
||||||
|
// hostName, exc.getMessage());
|
||||||
|
//RSEUIPlugin.logError("Disconnect failed",exc); // temporary
|
||||||
|
SystemMessageDialog msgDlg = new SystemMessageDialog(shell,
|
||||||
|
RSEUIPlugin.getPluginMessage(ISystemMessages.MSG_DISCONNECT_FAILED).makeSubstitution(hostName,exc));
|
||||||
|
msgDlg.setException(exc);
|
||||||
|
msgDlg.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show an error message when the user cancels the disconnection.
|
||||||
|
* Shows a common message by default.
|
||||||
|
* Overridable.
|
||||||
|
*/
|
||||||
|
protected void showDisconnectCancelledMessage(Shell shell, String hostName, int port)
|
||||||
|
{
|
||||||
|
//SystemMessage.displayMessage(SystemMessage.MSGTYPE_ERROR, shell, RSEUIPlugin.getResourceBundle(),
|
||||||
|
// ISystemMessages.MSG_DISCONNECT_CANCELLED, hostName);
|
||||||
|
SystemMessageDialog msgDlg = new SystemMessageDialog(shell,
|
||||||
|
RSEUIPlugin.getPluginMessage(ISystemMessages.MSG_DISCONNECT_CANCELLED).makeSubstitution(hostName));
|
||||||
|
msgDlg.open();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification from sub-services that our session was lost.
|
||||||
|
* Notify all subsystems properly.
|
||||||
|
* TODO allow user to try and reconnect?
|
||||||
|
*/
|
||||||
|
public void handleSessionLost() {
|
||||||
|
Activator.trace("SshConnectorService: handleSessionLost"); //$NON-NLS-1$
|
||||||
|
if (fSessionLostHandler!=null) {
|
||||||
|
fSessionLostHandler.sessionLost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Display getStandardDisplay() {
|
||||||
Display display = Display.getCurrent();
|
Display display = Display.getCurrent();
|
||||||
if( display==null ) {
|
if( display==null ) {
|
||||||
display = Display.getDefault();
|
display = Display.getDefault();
|
||||||
|
@ -404,7 +647,14 @@ public class SshConnectorService extends AbstractConnectorService implements ISs
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return (session!=null && session.isConnected());
|
if (session!=null) {
|
||||||
|
if (session.isConnected()) {
|
||||||
|
return true;
|
||||||
|
} else if (fSessionLostHandler!=null) {
|
||||||
|
fSessionLostHandler.sessionLost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasRemoteServerLauncherProperties() {
|
public boolean hasRemoteServerLauncherProperties() {
|
||||||
|
|
|
@ -25,23 +25,26 @@ __Usage:__
|
||||||
Window > Preferences > Team > CVS > SSh2 Connection Method > General
|
Window > Preferences > Team > CVS > SSh2 Connection Method > General
|
||||||
to set the ssh home directory, and private key types to be used.
|
to set the ssh home directory, and private key types to be used.
|
||||||
* Select the "Shells" node and choose Contextmenu > Launch Shell.
|
* Select the "Shells" node and choose Contextmenu > Launch Shell.
|
||||||
* Enter your username on the remote system. For the password, just
|
* Enter your username and password on the remote system. If you want
|
||||||
enter anything (this is not checked, since ssh has its own method
|
to use private-key authentication, just enter any dummy password -
|
||||||
of acquiring password information).
|
you will be asked for the passphrase of your keyring later, or
|
||||||
|
the connection will succeed without further prompting if your key
|
||||||
|
ring has an empty passphrase.
|
||||||
* When asked to accept remote host authenticity, press OK.
|
* When asked to accept remote host authenticity, press OK.
|
||||||
* Enter the correct password for ssh on the remote system (this is only
|
* Enter the correct password for ssh on the remote system (this is only
|
||||||
necessary if you are not using a private key).
|
necessary if you are not using a private key).
|
||||||
|
|
||||||
__Known Limitations:__
|
__Known Limitations:__
|
||||||
* Symbolic Links are not resolved (readlink not supported by jsch-0.1.28)
|
* Symbolic Links are not resolved (readlink not supported by jsch-0.1.28)
|
||||||
* Ssh passwords can not be stored (no key ring; use private keys instead)
|
|
||||||
* Ssh timeouts are not observed (no automatic reconnect)
|
* Ssh timeouts are not observed (no automatic reconnect)
|
||||||
- after auto-logout, shell just doesnt react to input any more
|
- after auto-logout, further actions will show a "connect cancel"
|
||||||
|
message and connection will go down
|
||||||
* Password and passphrase internal handling has not been checked for
|
* Password and passphrase internal handling has not been checked for
|
||||||
security against malicious reading from other Eclipse plugins.
|
security against malicious reading from other Eclipse plugins.
|
||||||
|
|
||||||
__Known Issues:__
|
__Known Issues:__
|
||||||
* A dummy password must be entered on initial connect (can be saved)
|
* A dummy password must be entered on initial connect, empty
|
||||||
|
password should be allowed if private key authentication is used
|
||||||
* After some time, the connection may freeze and need to be
|
* After some time, the connection may freeze and need to be
|
||||||
disconnected.
|
disconnected.
|
||||||
* Command service should be provided in addition to the remote shell service.
|
* Command service should be provided in addition to the remote shell service.
|
||||||
|
@ -49,14 +52,15 @@ __Known Issues:__
|
||||||
due to memory exhaustion (ArrayIndexOutOfBoundsException)
|
due to memory exhaustion (ArrayIndexOutOfBoundsException)
|
||||||
* "Break" can not be sent to the remote system in order to cancel
|
* "Break" can not be sent to the remote system in order to cancel
|
||||||
long-running jobs on the remote side.
|
long-running jobs on the remote side.
|
||||||
* Moving files with a space in the name doesn't currently work.
|
|
||||||
Renaming them works though.
|
|
||||||
* Copy&paste, or drag&drop of remote files remotely does'nt currently work.
|
|
||||||
* The plugin currently uses some "internal" classes from the
|
* The plugin currently uses some "internal" classes from the
|
||||||
org.eclipse.team.cvs.ui plugin. This needs to be cleaned up.
|
org.eclipse.team.cvs.ui plugin. This needs to be cleaned up.
|
||||||
* For other internal coding issues, see TODO items in the code.
|
* For other internal coding issues, see TODO items in the code.
|
||||||
|
|
||||||
__Changelog:__
|
__Changelog:__
|
||||||
|
v0.3:
|
||||||
|
* support Keyboard Interactive Authentication.
|
||||||
|
* Fix interaction with RSE passwords.
|
||||||
|
* Fix connection lost notifications
|
||||||
v0.2:
|
v0.2:
|
||||||
* Re-use Team/CVS/ssh2 preferences for ssh2 home and private keys specification
|
* Re-use Team/CVS/ssh2 preferences for ssh2 home and private keys specification
|
||||||
Allows to do the ssh login without password if private/public key are set up.
|
Allows to do the ssh login without password if private/public key are set up.
|
||||||
|
|
|
@ -4,5 +4,10 @@ import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
public interface ISshSessionProvider
|
public interface ISshSessionProvider
|
||||||
{
|
{
|
||||||
|
/* Return an active SSH session from a ConnectorService. */
|
||||||
public Session getSession();
|
public Session getSession();
|
||||||
|
|
||||||
|
/* Inform the connectorService that a session has been lost. */
|
||||||
|
public void handleSessionLost();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ public class SftpFileService extends AbstractFileService implements IFileService
|
||||||
return "Access a remote file system via Ssh / Sftp protocol";
|
return "Access a remote file system via Ssh / Sftp protocol";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO specify Exception more clearly
|
||||||
public void connect() throws Exception {
|
public void connect() throws Exception {
|
||||||
Activator.trace("SftpFileService.connecting..."); //$NON-NLS-1$
|
Activator.trace("SftpFileService.connecting..."); //$NON-NLS-1$
|
||||||
try {
|
try {
|
||||||
|
@ -77,16 +78,35 @@ public class SftpFileService extends AbstractFileService implements IFileService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ChannelSftp getChannel(String task) {
|
//TODO specify Exception more clearly
|
||||||
|
protected ChannelSftp getChannel(String task) throws Exception
|
||||||
|
{
|
||||||
Activator.trace(task);
|
Activator.trace(task);
|
||||||
if (fChannelSftp==null || !fChannelSftp.isConnected()) {
|
if (fChannelSftp==null || !fChannelSftp.isConnected()) {
|
||||||
Activator.trace(task + ": channel not connected: "+fChannelSftp); //$NON-NLS-1$
|
Activator.trace(task + ": channel not connected: "+fChannelSftp); //$NON-NLS-1$
|
||||||
|
Session session = fSessionProvider.getSession();
|
||||||
|
if (session!=null) {
|
||||||
|
if (!session.isConnected()) {
|
||||||
|
//notify of lost session. May reconnect asynchronously later.
|
||||||
|
fSessionProvider.handleSessionLost();
|
||||||
|
//dont throw an exception here, expect jsch to throw something useful
|
||||||
|
} else {
|
||||||
|
//session connected but channel not: try to reconnect
|
||||||
|
//(may throw Exception)
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO might throw NPE if session has been disconnected
|
||||||
}
|
}
|
||||||
return fChannelSftp;
|
return fChannelSftp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
getChannel("SftpFileService.disconnect").disconnect(); //$NON-NLS-1$
|
try {
|
||||||
|
getChannel("SftpFileService.disconnect").disconnect(); //$NON-NLS-1$
|
||||||
|
} catch(Exception e) {
|
||||||
|
/*nothing to do*/
|
||||||
|
}
|
||||||
fChannelSftp = null;
|
fChannelSftp = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +133,12 @@ public class SftpFileService extends AbstractFileService implements IFileService
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public boolean isConnected() {
|
||||||
return getChannel("SftpFileService.isConnected()").isConnected(); //$NON-NLS-1$
|
try {
|
||||||
|
return getChannel("SftpFileService.isConnected()").isConnected(); //$NON-NLS-1$
|
||||||
|
} catch(Exception e) {
|
||||||
|
/*cannot be connected when we cannot get a channel*/
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IHostFile[] internalFetch(IProgressMonitor monitor, String parentPath, String fileFilter, int fileType)
|
protected IHostFile[] internalFetch(IProgressMonitor monitor, String parentPath, String fileFilter, int fileType)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.rse.services.shells.IHostShellOutputReader;
|
||||||
import org.eclipse.rse.services.ssh.ISshSessionProvider;
|
import org.eclipse.rse.services.ssh.ISshSessionProvider;
|
||||||
|
|
||||||
import com.jcraft.jsch.Channel;
|
import com.jcraft.jsch.Channel;
|
||||||
|
import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Shell subsystem for SSH.
|
* A Shell subsystem for SSH.
|
||||||
|
@ -62,12 +63,22 @@ public class SshHostShell extends AbstractHostShell {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
return !fChannel.isEOF();
|
if (fChannel!=null && !fChannel.isEOF()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// shell is not active: check for session lost
|
||||||
|
Session session = fSessionProvider.getSession();
|
||||||
|
if (session!=null && !session.isConnected()) {
|
||||||
|
fSessionProvider.handleSessionLost();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeToShell(String command) {
|
public void writeToShell(String command) {
|
||||||
fStdinHandler.println(command);
|
if (isActive()) {
|
||||||
fStdinHandler.flush();
|
fStdinHandler.println(command);
|
||||||
|
fStdinHandler.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHostShellOutputReader getStandardOutputReader() {
|
public IHostShellOutputReader getStandardOutputReader() {
|
||||||
|
|
|
@ -40,6 +40,12 @@ public class SshShellOutputReader extends AbstractHostShellOutputReader
|
||||||
fReader = reader;
|
fReader = reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
//check for active session and notify lost session if necessary
|
||||||
|
getHostShell().isActive();
|
||||||
|
}
|
||||||
|
|
||||||
protected Object internalReadLine() {
|
protected Object internalReadLine() {
|
||||||
if (fReader == null) {
|
if (fReader == null) {
|
||||||
//Our workaround sets the stderr reader to null, so we never give any stderr output.
|
//Our workaround sets the stderr reader to null, so we never give any stderr output.
|
||||||
|
|
Loading…
Add table
Reference in a new issue