blob: 3485055c882570fbf8e74b5b26becbab10a8dcf8 [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 org.eclipse.ptp.remotetools.core.messages.Messages;
import org.eclipse.ptp.remotetools.exception.RemoteConnectionException;
import org.eclipse.ptp.remotetools.internal.common.AbstractRemoteExecution;
import org.eclipse.ptp.remotetools.internal.common.Debug;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
/**
* A remote execution that can be canceled by killing the running bash command.
*
* @author Daniel Felix Ferber
*
*/
public abstract class KillableExecution extends AbstractRemoteExecution {
private ChannelExec channel;
/**
* Internal process identifier.
*/
private int PIID;
/**
* Process id on the remote host.
*/
private int PID;
public KillableExecution(ExecutionManager executionManager) throws RemoteConnectionException {
super(executionManager);
}
public void startExecution() throws RemoteConnectionException {
try {
getExecutionManager().registerOperation(this);
getExecutionManager().getConnection().registerObservedExecution(this);
channel.connect();
} catch (JSchException e) {
throw new RemoteConnectionException(e.getLocalizedMessage());
}
}
@Override
protected void notifyCancel() {
/*
* Stop observing this channel.
*/
getExecutionManager().unregisterOperation(this);
getExecutionManager().getConnection().unregisterObservedExecution(this);
/*
* Force the end of the execution by closing the channel and killing
* (SIGHUP or something similar) on the remote host. Remove the channel
* from the connection pool.
*/
getExecutionManager().getConnection().killExecution(this);
getExecutionManager().getConnection().releaseChannel(channel);
super.notifyCancel();
}
@Override
protected void notifyFinish() {
/*
* Stop observing this channel. Remote the channel from the connection
* pool
*/
getExecutionManager().unregisterOperation(this);
getExecutionManager().getConnection().unregisterObservedExecution(this);
getExecutionManager().getConnection().releaseChannel(channel);
super.notifyFinish();
}
@Override
public void close() {
/*
* Cancel execution, if still not finished. Then, make sure the channel
* is closed and released from the execution manager.
*/
if (isRunning()) {
cancel();
}
super.close();
}
protected ChannelExec createChannel(boolean hasPTY) throws RemoteConnectionException {
/*
* Get a channel from the connection pool. Channels with PTY must be
* managed by the pool.
*/
channel = getExecutionManager().getConnection().createExecChannel(hasPTY);
channel.setPty(hasPTY);
return channel;
}
/**
* Create a killable command line that will run on any system.
*
* 1. The whole command is run using /bin/sh to ensure that it will work on
* any system. 2. The killable prefix echos the PID of the shell to the
* control terminal. This is read by the connection manager and can be used
* to send a kill signal in order to terminate the process.
*
* NOTE: there is a maximum line length on most systems. If this is exceeded
* the command will fail.
*
* @param commandLine
* command line to run
*/
protected void setCommandLine(String commandLine) {
PIID = getExecutionManager().getConnection().createNextPIID();
String newCommandLine = "/bin/sh -c '" //$NON-NLS-1$
+ getExecutionManager().getConnection().getKillablePrefix(this) + "; " //$NON-NLS-1$
+ commandLine + "'"; //$NON-NLS-1$
Debug.println2(Messages.KillableExecution_Debug_1 + newCommandLine);
channel.setCommand(newCommandLine);
}
public int getReturnCode() {
if (wasCanceled()) {
return -1;
} else if (!wasFinished()) {
throw new IllegalStateException();
}
return channel.getExitStatus();
}
public int getPID() {
return PID;
}
public void setPID(int pid) {
PID = pid;
}
public int getInternalID() {
return PIID;
}
public boolean isRunning() {
return !channel.isClosed();
}
public int getFinishStatus() {
int code = getReturnCode();
if (code == 0) {
return SUCCESS_OK;
} else if (code <= 125) {
return SUCCESS_ERROR;
} else if (code == 126) {
return ERROR_NOT_EXECUTABLE;
} else if (code == 127) {
return ERROR_NOT_FOUND;
} else if (code == 128) {
return UNKNOWN;
} else if (code == 255) {
return INVALID_EXIT_CODE;
} else if (code == 128 + 1) {
return SIGHUP;
} else if (code == 128 + 2) {
return SIGINT;
} else if (code == 128 + 3) {
return SIGQUIT;
} else if (code == 128 + 4) {
return SIGILL;
} else if (code == 128 + 5) {
return SIGTRAP;
} else if (code == 128 + 6) {
return SIGIOT;
} else if (code == 128 + 7) {
return SIGBUS;
} else if (code == 128 + 8) {
return SIGFPE;
} else if (code == 128 + 9) {
return SIGKILL;
} else if (code == 128 + 10) {
return SIGUSR1;
} else if (code == 128 + 11) {
return SIGSEGV;
} else if (code == 128 + 12) {
return SIGUSR2;
} else if (code == 128 + 13) {
return SIGPIPE;
} else if (code == 128 + 14) {
return SIGALRM;
} else if (code == 128 + 15) {
return SIGTERM;
} else if (code == 128 + 16) {
return SIGSTKFLT;
} else if (code == 128 + 17) {
return SIGCHLD;
} else if (code == 128 + 18) {
return SIGCONT;
} else if (code == 128 + 19) {
return SIGSTOP;
} else if (code == 128 + 20) {
return SIGTSTP;
} else if (code == 128 + 21) {
return SIGTTIN;
} else if (code == 128 + 22) {
return SIGTTOU;
} else if (code == 128 + 23) {
return SIGURG;
} else if (code == 128 + 24) {
return SIGXCPU;
} else if (code == 128 + 25) {
return SIGXFSZ;
} else if (code == 128 + 26) {
return SIGVTALRM;
} else if (code == 128 + 27) {
return SIGPROF;
} else if (code == 128 + 28) {
return SIGWINCH;
} else if (code == 128 + 29) {
return SIGIO;
} else if (code == 128 + 30) {
return SIGPWR;
} else {
return UNKNOWN;
}
}
public String getFinishStatusText(int status) {
switch (status) {
case SUCCESS_OK:
return Messages.KillableExecution_FinishStatus_Ok;
case SUCCESS_ERROR:
return Messages.KillableExecution_FinishStatus_Error;
case ERROR_NOT_EXECUTABLE:
return Messages.KillableExecution_FinishStatus_NotExecutable;
case ERROR_NOT_FOUND:
return Messages.KillableExecution_FinishStatus_CommandNotFound;
case INVALID_EXIT_CODE:
return Messages.KillableExecution_FinishStatus_InvalidExitCode;
case SIGHUP:
return Messages.KillableExecution_FinishStatus_Hangup;
case SIGINT:
return Messages.KillableExecution_FinishStatus_TerminalInterrupt;
case SIGQUIT:
return Messages.KillableExecution_FinishStatus_TerminalQuit;
case SIGILL:
return Messages.KillableExecution_FinishStatus_IllegalInstruction;
case SIGTRAP:
return Messages.KillableExecution_FinishStatus_TraceTrap;
case SIGIOT:
return Messages.KillableExecution_FinishStatus_IOTTrap;
case SIGBUS:
return Messages.KillableExecution_FinishStatus_BUSError;
case SIGFPE:
return Messages.KillableExecution_FinishStatus_FloatingPointException;
case SIGKILL:
return Messages.KillableExecution_FinishStatus_Kill;
case SIGUSR1:
return Messages.KillableExecution_FinishStatus_UserDefinedSignal1;
case SIGSEGV:
return Messages.KillableExecution_FinishStatus_InvalidMemorySegmentAccess;
case SIGUSR2:
return Messages.KillableExecution_FinishStatus_UserDefinedSignal2;
case SIGPIPE:
return Messages.KillableExecution_FinishStatus_BrokenPipe;
case SIGALRM:
return Messages.KillableExecution_FinishStatus_AlarmClock;
case SIGTERM:
return Messages.KillableExecution_FinishStatus_Termination;
case SIGSTKFLT:
return Messages.KillableExecution_FinishStatus_StackFault;
case SIGCHLD:
return Messages.KillableExecution_FinishStatus_ChildProcessStoppedOrExited;
case SIGCONT:
return Messages.KillableExecution_FinishStatus_ContinueExecuting;
case SIGSTOP:
return Messages.KillableExecution_FinishStatus_StopExecuting;
case SIGTSTP:
return Messages.KillableExecution_FinishStatus_TerminalStopSignal;
case SIGTTIN:
return Messages.KillableExecution_FinishStatus_BackgroundProcessReadTTY;
case SIGTTOU:
return Messages.KillableExecution_FinishStatus_BackgroundWriteTTY;
case SIGURG:
return Messages.KillableExecution_FinishStatus_UrgentConditionSocket;
case SIGXCPU:
return Messages.KillableExecution_FinishStatus_CPULimitExceeded;
case SIGXFSZ:
return Messages.KillableExecution_FinishStatus_FileSizeLimitExceeded;
case SIGVTALRM:
return Messages.KillableExecution_FinishStatus_VirtualAlarmClock;
case SIGPROF:
return Messages.KillableExecution_FinishStatus_ProfilingAlarmClock;
case SIGWINCH:
return Messages.KillableExecution_FinishStatus_WindowSizeChange;
case SIGIO:
return Messages.KillableExecution_FinishStatus_IOPossible;
case SIGPWR:
return Messages.KillableExecution_FinishStatus_PowerFailureRestart;
default:
return Messages.KillableExecution_FinishStatus_Unknown;
}
}
public boolean isException(int status) {
return (status >= 129) && (status <= 158);
}
public boolean isOK(int status) {
return status <= 125;
}
public boolean isExecutableError(int status) {
return (status >= 126) && (status <= 128);
}
public boolean wasException() {
return isException(getReturnCode());
}
public boolean wasOK() {
return isOK(getReturnCode());
}
public boolean wasCommandError() {
return isExecutableError(getReturnCode());
}
public static final int UNKNOWN = 0;
public static final int SUCCESS_OK = 1;
public static final int SUCCESS_ERROR = 2;
public static final int ERROR_NOT_EXECUTABLE = 126;
public static final int ERROR_NOT_FOUND = 127;
public static final int INVALID_EXIT_CODE = 128;
public static final int SIGHUP = 129;
public static final int SIGINT = 130;
public static final int SIGQUIT = 131;
public static final int SIGILL = 132;
public static final int SIGTRAP = 133;
public static final int SIGIOT = 134;
public static final int SIGBUS = 135;
public static final int SIGFPE = 136;
public static final int SIGKILL = 137;
public static final int SIGUSR1 = 138;
public static final int SIGSEGV = 139;
public static final int SIGUSR2 = 140;
public static final int SIGPIPE = 141;
public static final int SIGALRM = 142;
public static final int SIGTERM = 143;
public static final int SIGSTKFLT = 144;
public static final int SIGCHLD = 145;
public static final int SIGCONT = 146;
public static final int SIGSTOP = 147;
public static final int SIGTSTP = 148;
public static final int SIGTTIN = 149;
public static final int SIGTTOU = 150;
public static final int SIGURG = 151;
public static final int SIGXCPU = 152;
public static final int SIGXFSZ = 153;
public static final int SIGVTALRM = 154;
public static final int SIGPROF = 155;
public static final int SIGWINCH = 156;
public static final int SIGIO = 157;
public static final int SIGPWR = 158;
}