blob: 840d828666c0ece760bfca0953fcb41aa6ab4cb1 [file] [log] [blame]
/*******************************************************************************
* 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 contribution
*******************************************************************************/
package org.eclipse.remote.telnet.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import org.eclipse.remote.core.IRemoteConnection;
import org.eclipse.remote.core.IRemoteConnectionHostService;
import org.eclipse.remote.core.IRemoteProcess;
import org.eclipse.remote.core.IRemoteProcessBuilder;
import org.eclipse.remote.core.exception.RemoteConnectionException;
import org.eclipse.remote.telnet.internal.core.Logger;
import org.eclipse.remote.telnet.internal.core.messages.Messages;
public class TelnetCommandShell implements IRemoteProcess {
private final TelnetConnection telnetConnection;
private TelnetProtocol protocol;
public TelnetCommandShell(IRemoteConnection remoteConnection, TelnetConnection telnetConnection) {
this.telnetConnection = telnetConnection;
assert(remoteConnection.getService(IRemoteConnectionHostService.class) != null);
}
@Override
public void destroy() {
if (protocol != null) {
protocol.interrupt();
}
}
@Override
public int exitValue() {
return 0;
}
@Override
public InputStream getErrorStream() {
return null;
}
@Override
public InputStream getInputStream() {
if (protocol != null) {
PipedOutputStream pipedOutput = new PipedOutputStream();
protocol.setClientOutputStream(pipedOutput);
try {
return new PipedInputStream(pipedOutput);
} catch (IOException e) {
return null;
}
}
return null;
}
@Override
public OutputStream getOutputStream() {
if (protocol != null) {
return protocol.getOutputStream();
}
return null;
}
@Override
public int waitFor() throws InterruptedException {
if (protocol != null && protocol.isConnected()) {
wait();
}
return 0;
}
@Override
public boolean isCompleted() {
return protocol == null || !protocol.isAlive();
}
@Override
public IRemoteConnection getRemoteConnection() {
return telnetConnection.getRemoteConnection();
}
@Override
public <T extends Service> T getService(Class<T> service) {
return null;
}
@Override
public <T extends Service> boolean hasService(Class<T> service) {
return false;
}
@Override
public IRemoteProcessBuilder getProcessBuilder() {
return null;
}
public void connect() throws RemoteConnectionException {
IRemoteConnectionHostService hostSvc = telnetConnection.getRemoteConnection()
.getService(IRemoteConnectionHostService.class);
// Retry the connect after a little pause in case the
// remote telnet server isn't ready. ConnectExceptions might
// happen if the telnet server process did not initialized itself.
// This is seen especially if the telnet server is a process
// providing it's input and output via a built in telnet server.
int remaining = 10;
while (remaining >= 0) {
// Pause before we re-try if the remaining tries are less than the initial value
if (remaining < 10) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
/* ignored on purpose */ }
}
try {
int nTimeout = hostSvc.getTimeout() * 1000;
String strHost = hostSvc.getHostname();
int nPort = hostSvc.getPort();
InetSocketAddress address = new InetSocketAddress(strHost, nPort);
Socket socket = new Socket();
socket.connect(address, nTimeout);
// If we get to this point, the connect succeeded and we will
// force the remaining counter to be 0.
remaining = 0;
// This next call causes reads on the socket to see TCP urgent data
// inline with the rest of the non-urgent data. Without this call, TCP
// urgent data is silently dropped by Java. This is required for
// TELNET support, because when the TELNET server sends "IAC DM", the
// IAC byte is TCP urgent data. If urgent data is silently dropped, we
// only see the DM, which looks like an ISO Latin-1 '�' character.
socket.setOOBInline(true);
socket.setKeepAlive(true);
protocol = new TelnetProtocol(socket, this);
protocol.start();
} catch (UnknownHostException ex) {
// No re-try in case of UnknownHostException, there is no indication that
// the DNS will fix itself
throw new RemoteConnectionException(Messages.TelnetCommandShell_0 + ex.getMessage());
} catch (SocketTimeoutException socketTimeoutException) {
// Time out occurred. No re-try in this case either. Time out can
// be increased by the user. Multiplying the timeout with the remaining
// counter is not desired.
throw new RemoteConnectionException(socketTimeoutException.getMessage());
} catch (ConnectException connectException) {
// In case of a ConnectException, do a re-try. The server could have been
// simply not ready yet and the worker would give up to early. If the terminal
// control is already closed (disconnected), don't print "Connection refused" errors
if (remaining == 0) {
throw new RemoteConnectionException(connectException.getMessage());
}
} catch (Exception exception) {
// Any other exception on connect. No re-try in this case either
// Log the exception
Logger.logException(exception);
// And signal failed
throw new RemoteConnectionException(exception.getMessage());
} finally {
remaining--;
}
}
}
protected void terminated() {
telnetConnection.terminated(this);
}
}