blob: 14d21a4e960e6af5c9ad9661e1a88afd772bad12 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2013, 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 Implementation
* Martin Oberhuber - [468889] Support Eclipse older than Mars
*****************************************************************************/
package org.eclipse.remote.internal.jsch.core.commands;
import java.io.IOException;
import java.text.CharacterIterator;
import java.text.MessageFormat;
import java.text.StringCharacterIterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.provider.FileInfo;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.remote.core.RemoteServicesUtils;
import org.eclipse.remote.core.exception.RemoteConnectionException;
import org.eclipse.remote.internal.jsch.core.JSchConnection;
import org.eclipse.remote.internal.jsch.core.messages.Messages;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpProgressMonitor;
/**
* @author greg
*
*/
public abstract class AbstractRemoteCommand<T> {
protected static class CommandProgressMonitor implements SftpProgressMonitor {
private final IProgressMonitor fMonitor;
private double fWorkPercentFactor;
private Long fMaxWork;
private String fMaxWorkSize;
private long fWorkToDate;
private String fPrefix;
public CommandProgressMonitor(IProgressMonitor monitor) {
fMonitor = monitor;
}
public CommandProgressMonitor(String prefix, IProgressMonitor monitor) {
fPrefix = prefix;
fMonitor = monitor;
}
@Override
public boolean count(long count) {
fWorkToDate += count;
String size;
Long workToDate;
if (fWorkToDate < 1024L) {
size = "bytes"; //$NON-NLS-1$
workToDate = fWorkToDate;
} else {
size = "KB"; //$NON-NLS-1$
workToDate = fWorkToDate / 1024L;
}
StringBuffer taskName = new StringBuffer();
if (fPrefix != null) {
taskName.append(fPrefix);
}
if (fWorkPercentFactor < 0) {
taskName.append(MessageFormat.format(Messages.AbstractRemoteCommand_format1, new Object[] { workToDate, size }));
} else {
Double workPercent = Double.valueOf(fWorkPercentFactor * fWorkToDate);
taskName.append(MessageFormat.format(Messages.AbstractRemoteCommand_format2,
new Object[] { workToDate, size, fMaxWork, fMaxWorkSize, workPercent }));
}
fMonitor.subTask(taskName.toString());
fMonitor.worked((int) count);
return !(fMonitor.isCanceled());
}
@Override
public void end() {
fMonitor.done();
}
@Override
public void init(int op, String src, String dest, long max) {
fWorkPercentFactor = 1.0 / max;
if (max < 1024L) {
fMaxWorkSize = "bytes"; //$NON-NLS-1$
fMaxWork = max;
} else {
fMaxWorkSize = "KB"; //$NON-NLS-1$
fMaxWork = max / 1024L;
}
fWorkToDate = 0;
fMonitor.beginTask(RemoteServicesUtils.posixPath(src).lastSegment(), (int) max);
}
}
protected abstract class ExecCallable<T1> implements Callable<T1> {
private IProgressMonitor fProgressMonitor;
private ChannelExec fExecChannel;
private Future<T1> asyncCmdInThread() throws RemoteConnectionException {
setChannel(fConnection.getExecChannel());
return fPool.submit(this);
}
/*
* (non-Javadoc)
*
* @see java.util.concurrent.Callable#call()
*/
@Override
public abstract T1 call() throws JSchException, IOException, RemoteConnectionException;
private void finalizeCmdInThread() {
setChannel(null);
}
public ChannelExec getChannel() {
return fExecChannel;
}
public IProgressMonitor getProgressMonitor() {
return fProgressMonitor;
}
/**
* Function opens exec channel and then executes the exec operation. If
* run on the main thread it executes it on a separate thread
*/
public T1 getResult(IProgressMonitor monitor) throws RemoteConnectionException {
Future<T1> future = null;
fProgressMonitor = SubMonitor.convert(monitor, 10);
try {
future = asyncCmdInThread();
return waitCmdInThread(future);
} finally {
finalizeCmdInThread();
}
}
public void setChannel(ChannelExec channel) {
fExecChannel = channel;
}
private T1 waitCmdInThread(Future<T1> future) throws RemoteConnectionException {
boolean bInterrupted = Thread.interrupted();
while (!getProgressMonitor().isCanceled()) {
try {
return future.get(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
bInterrupted = true;
} catch (TimeoutException e) {
// ignore
} catch (ExecutionException e) {
getChannel().disconnect();
throw new RemoteConnectionException(e.getMessage());
}
getProgressMonitor().worked(1);
}
if (bInterrupted) {
Thread.currentThread().interrupt(); // set current thread flag
}
future.cancel(true);
getChannel().disconnect();
throw new RemoteConnectionException(Messages.AbstractRemoteCommand_Operation_cancelled_by_user);
}
}
protected abstract class SftpCallable<T1> implements Callable<T1> {
private IProgressMonitor fProgressMonitor;
private ChannelSftp fSftpChannel;
private Future<T1> asyncCmdInThread() throws RemoteConnectionException {
setChannel(fConnection.getSftpCommandChannel());
return fPool.submit(this);
}
/*
* (non-Javadoc)
*
* @see java.util.concurrent.Callable#call()
*/
@Override
public abstract T1 call() throws JSchException, SftpException, IOException;
private void finalizeCmdInThread() {
setChannel(null);
}
public ChannelSftp getChannel() {
return fSftpChannel;
}
public IProgressMonitor getProgressMonitor() {
return fProgressMonitor;
}
/**
* Function opens sftp channel and then executes the sftp operation. If
* run on the main thread it executes it on a separate thread
*/
public T1 getResult(IProgressMonitor monitor) throws SftpException, RemoteConnectionException {
Future<T1> future = null;
fProgressMonitor = SubMonitor.convert(monitor, 10);
try {
future = asyncCmdInThread();
return waitCmdInThread(future);
} finally {
finalizeCmdInThread();
}
}
public void setChannel(ChannelSftp channel) {
fSftpChannel = channel;
}
private T1 waitCmdInThread(Future<T1> future) throws SftpException, RemoteConnectionException {
boolean bInterrupted = Thread.interrupted();
while (!getProgressMonitor().isCanceled()) {
try {
return future.get(100, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
bInterrupted = true;
} catch (TimeoutException e) {
// ignore
} catch (ExecutionException e) {
if (e.getCause() instanceof SftpException) {
throw (SftpException) e.getCause();
}
throw new RemoteConnectionException(e.getMessage());
}
getProgressMonitor().worked(1);
}
if (bInterrupted) {
Thread.currentThread().interrupt(); // set current thread flag
}
future.cancel(true);
throw new RemoteConnectionException(Messages.AbstractRemoteCommand_Operation_cancelled_by_user);
}
}
private static ExecutorService fPool = Executors.newSingleThreadExecutor();
private final JSchConnection fConnection;
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;
public AbstractRemoteCommand(JSchConnection connection) {
fConnection = connection;
}
protected IFileInfo convertToFileInfo(final IPath path, SftpATTRS attrs, IProgressMonitor monitor)
throws RemoteConnectionException {
return convertToFileInfo(path.lastSegment(), path.removeLastSegments(1), attrs, monitor);
}
protected IFileInfo convertToFileInfo(final String name, final IPath parentPath, SftpATTRS attrs, IProgressMonitor monitor)
throws RemoteConnectionException {
SubMonitor progress = SubMonitor.convert(monitor, 10);
FileInfo fileInfo = new FileInfo(name);
fileInfo.setExists(true);
fileInfo.setDirectory(attrs.isDir());
fileInfo.setAttribute(EFS.ATTRIBUTE_OWNER_EXECUTE, (attrs.getPermissions() & 0100) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_OWNER_WRITE, (attrs.getPermissions() & 0200) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_OWNER_READ, (attrs.getPermissions() & 0400) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_EXECUTE, (attrs.getPermissions() & 0010) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_WRITE, (attrs.getPermissions() & 0020) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_READ, (attrs.getPermissions() & 0040) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_EXECUTE, (attrs.getPermissions() & 0001) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_WRITE, (attrs.getPermissions() & 0002) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_READ, (attrs.getPermissions() & 0004) != 0);
fileInfo.setAttribute(EFS.ATTRIBUTE_SYMLINK, attrs.isLink());
if (attrs.isLink()) {
SftpCallable<String> c2 = new SftpCallable<String>() {
@Override
public String call() throws JSchException, SftpException {
return getChannel().readlink(parentPath.append(name).toString());
}
};
String target;
try {
progress.subTask(Messages.AbstractRemoteCommand_Get_symlink_target);
target = c2.getResult(progress.newChild(10));
fileInfo.setStringAttribute(EFS.ATTRIBUTE_LINK_TARGET, target);
} catch (SftpException e) {
// Ignore
}
}
fileInfo.setLastModified(attrs.getMTime() * 1000L);
fileInfo.setLength(attrs.getSize());
return fileInfo;
}
public JSchConnection getConnection() {
return fConnection;
}
public int getFinishStatus() {
int code = 0;
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;
}
}
protected abstract T getResult(IProgressMonitor monitor) throws RemoteConnectionException;
protected String quote(String path, boolean full) {
StringBuffer buffer = new StringBuffer();
StringCharacterIterator iter = new StringCharacterIterator(path);
for (char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
switch (c) {
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '|':
case '\\':
case '*':
case '&':
case '^':
case '%':
case '$':
case '#':
case '@':
case '!':
case '~':
case '`':
case '\'':
case '"':
case ':':
case ';':
case '?':
case '<':
case '>':
case ',':
case '\n':
if (full) {
buffer.append('\\');
}
buffer.append(c);
continue;
case ' ':
buffer.append('\\');
buffer.append(c);
continue;
default:
buffer.append(c);
continue;
}
}
return buffer.toString();
}
}