blob: 2e0dc4ea3ad61fafc4de002d4579601bbc5e7325 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006 IBM Corporation.
* 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 Implementation
*
*****************************************************************************/
package org.eclipse.ptp.remotetools.internal.ssh;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.ptp.remotetools.exception.RemoteConnectionException;
import org.eclipse.ptp.remotetools.internal.common.Debug;
import org.eclipse.ptp.remotetools.utils.stream.ILineStreamListener;
import org.eclipse.ptp.remotetools.utils.stream.TextStreamObserver;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
public class ControlChannel implements ILineStreamListener {
/*
* Patterns recognized by the observer.
*/
final static String marker = "//"; //$NON-NLS-1$
final static String markerPID = "PID:"; //$NON-NLS-1$
final static String markerPIID = "PIID:"; //$NON-NLS-1$
final static String markerSSH = "SSH_TTY:"; //$NON-NLS-1$
final Pattern pidPattern = Pattern.compile("^" + marker + markerPID + "\\p{Digit}+" + marker + markerPIID //$NON-NLS-1$ //$NON-NLS-2$
+ "\\p{Digit}+" + marker + "$"); //$NON-NLS-1$ //$NON-NLS-2$
final Pattern pidFilterPattern = Pattern.compile("\\p{Digit}+"); //$NON-NLS-1$
final Pattern terminalPathPattern = Pattern.compile("^" + marker + markerSSH + ".+" + marker + "$"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
// OutputStream that sends input to remote process input stream.
OutputStream outputToControlTerminalInput;
InputStream inputFromControlTerminalOutput;
// Control channel
ChannelExec shell;
// Control terminal path.
String controlTerminalPath;
// Control terminal observer thread.
TextStreamObserver controlTerminalObserver;
/**
* Parent execution manager who will be notified about events from the control channel.
*/
private Connection connection;
public ControlChannel(Connection connection) {
this.connection = connection;
}
public void open() throws RemoteConnectionException {
try{
// Open exec channel and alloc terminal
shell = connection.createExecChannel(false);
shell.setPty(true);
shell.setCommand("/bin/bash"); //$NON-NLS-1$
inputFromControlTerminalOutput = shell.getInputStream();
outputToControlTerminalInput = shell.getOutputStream();
shell.connect();
} catch (JSchException e) {
close();
throw new RemoteConnectionException(Messages.ControlChannel_Open_FailedCreateAuxiliaryShell, e);
} catch (IOException e) {
close();
throw new RemoteConnectionException(Messages.ControlChannel_Open_FailedCreateIOStream, e);
}
// Create stream observer and start it
controlTerminalObserver = new TextStreamObserver(inputFromControlTerminalOutput, this);
controlTerminalObserver.start();
try {
// Clean shell prompt.
outputToControlTerminalInput.write("export PS1=\n".getBytes()); //$NON-NLS-1$
// Write terminal path on control channel, to be read by the observer
// "//SSH_TTY: $SSH_TTY//\"\n"
outputToControlTerminalInput.write(new String("echo \"" + marker + markerSSH + "$SSH_TTY" + marker + "\"\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
//outputToControlTerminalInput.write(new String("echo helloworld > /tmp/helloworld\n").getBytes());
outputToControlTerminalInput.flush();
} catch (IOException e) {
throw new RemoteConnectionException(Messages.ControlChannel_Open_FailedSendInitCommands, e);
}
// Wait until the channel answers the terminal path
Debug.println2(Messages.ControlChannel_Debug_StartedWaitingControlTerminalPath);
synchronized (this) {
while (controlTerminalPath == null) {
try {
this.wait(200);
} catch (InterruptedException e) {
throw new RemoteConnectionException(Messages.ControlChannel_Open_WaitControlTerminalPathInterrupted, e);
}
}
}
Debug.println2(Messages.ControlChannel_Debug_ReceivedControlTerminalPath + controlTerminalPath);
}
public void newLine(String line) {
Debug.println2(Messages.ControlChannel_Debug_ControlConnectionReceived + line);
/*
* Test if the line contains a PID.
*/
Matcher pidmatch = pidPattern.matcher(line);
if (pidmatch.find()) {
Matcher pidmatch2 = pidFilterPattern.matcher(line);
pidmatch2.find();
String temppid = pidmatch2.group();
pidmatch2.find();
String temppiid = pidmatch2.group();
connection.setPID(Integer.parseInt(temppiid), Integer.parseInt(temppid));
return;
}
Matcher terminalPathMatcher = terminalPathPattern.matcher(line);
if (terminalPathMatcher.find()) {
int pre = new String(marker + markerSSH).length();
int pos = marker.length();
synchronized (this) {
controlTerminalPath = line.substring(pre, line.length() - pos);
this.notifyAll();
}
}
}
public void streamClosed() {
}
public void streamError(Exception e) {
Debug.println2(Messages.ControlChannel_Debug_ControlConnectionReceived + e); //$NON-NLS-1$
}
public synchronized String getControlTerminalPath() {
return controlTerminalPath;
}
public void killRemoteProcess(int pid) {
try {
outputToControlTerminalInput.write(new String("kill -9 " + pid + "\n").getBytes()); //$NON-NLS-1$ //$NON-NLS-2$
outputToControlTerminalInput.flush();
} catch (IOException e) {
}
}
public synchronized String getKillablePrefix(int internaID) {
return "echo " + marker + markerPID + "$$" + marker + markerPIID + Integer.toString(internaID) + marker + " > " //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ controlTerminalPath;
}
public void close() {
if (controlTerminalObserver != null) {
controlTerminalObserver.kill();
}
controlTerminalObserver = null;
outputToControlTerminalInput = null;
inputFromControlTerminalOutput = null;
if (shell != null) {
shell.disconnect();
}
shell = null;
connection = null;
}
}