blob: a92b9468a99992c787442ac9f328878c3f12560c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 Oak Ridge National Laboratory 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:
* John Eblen - initial implementation
*******************************************************************************/
package org.eclipse.ptp.rdt.sync.git.core;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ptp.remote.core.IRemoteConnection;
import org.eclipse.ptp.remote.core.IRemoteFileManager;
import org.eclipse.ptp.remote.core.IRemoteProcess;
import org.eclipse.ptp.remote.core.IRemoteProcessBuilder;
import org.eclipse.ptp.remote.core.exception.RemoteConnectionException;
// Static class for running system-level commands. This includes local and remote directory operations and also running arbitrary
// commands on remote machines.
public class CommandRunner {
// Nested convenience class for storing the results of a command.
public static class CommandResults {
private String stdout;
private String stderr;
private int exitCode;
/**
* @return the exitCode
*/
public int getExitCode() {
return exitCode;
}
/**
* @return the stderr
*/
public String getStderr() {
return stderr;
}
/**
* @return the stdout
*/
public String getStdout() {
return stdout;
}
/**
* @param exitCode
*/
public void setExitCode(int exitCode) {
this.exitCode = exitCode;
}
/**
* @param stderr
*/
public void setStderr(String stderr) {
this.stderr = stderr;
}
/**
* @param stdout
*/
public void setStdout(String stdout) {
this.stdout = stdout;
}
};
enum DirectoryStatus {
NOT_A_DIRECTORY, NOT_PRESENT, PRESENT
}
/**
* Simply check if a local directory is present but do not create
*
* @return the directory status
*/
public static DirectoryStatus checkLocalDirectory(String localDirectory) {
final File localDir = new File(localDirectory);
if (localDir.exists() == false) {
return DirectoryStatus.NOT_PRESENT;
}
else if (localDir.isDirectory()) {
return DirectoryStatus.PRESENT;
}
return DirectoryStatus.NOT_A_DIRECTORY;
}
/**
* Simply check if a remote directory is present but do not create
*
* @param conn
* @param remoteDir
* @return the directory status
*/
public static DirectoryStatus checkRemoteDirectory(IRemoteConnection conn, String remoteDir) {
final IRemoteFileManager fileManager = conn.getRemoteServices().getFileManager(conn);
final IFileStore fileStore = fileManager.getResource(remoteDir);
final IFileInfo fileInfo = fileStore.fetchInfo();
if (fileInfo.exists() == false) {
return DirectoryStatus.NOT_PRESENT;
} else if (fileInfo.isDirectory() == false) {
return DirectoryStatus.NOT_A_DIRECTORY;
} else {
return DirectoryStatus.PRESENT;
}
}
/**
* This function creates the local directory if it does not exist.
*
* @return whether the directory was already PRESENT
* TODO: Handle false return from mkdir
*/
public static DirectoryStatus createLocalDirectory(String localDirectory) {
final DirectoryStatus directoryStatus = checkLocalDirectory(localDirectory);
if (directoryStatus == DirectoryStatus.NOT_PRESENT) {
final File localDir = new File(localDirectory);
localDir.mkdir();
}
return directoryStatus;
}
/**
* This function creates the remote directory if it does not exist. Parent directories are also created if necessary. Note that
* this command does not overwrite if the requested remote directory exists but is not a directory.
*
* TODO: Check that this also holds true for creating parent directories. For example, if remoteDir =
* "/home/user/project/project1/" and "/home/user/project" is a file will it be deleted?
* @param conn
* @param remoteDir
* @throws CoreException
* on problem creating the remote directory.
* @return whether the directory was already PRESENT
*/
public static DirectoryStatus createRemoteDirectory(IRemoteConnection conn, String remoteDir) throws CoreException {
final IRemoteFileManager fileManager = conn.getRemoteServices().getFileManager(conn);
final IFileStore fileStore = fileManager.getResource(remoteDir);
final IFileInfo fileInfo = fileStore.fetchInfo();
if (fileInfo.exists() == true) {
if (fileInfo.isDirectory() == true) {
return DirectoryStatus.PRESENT;
} else {
return DirectoryStatus.NOT_A_DIRECTORY;
}
}
try {
fileStore.mkdir(EFS.NONE, null);
} catch (final CoreException e) {
throw e;
}
return DirectoryStatus.NOT_PRESENT;
}
/**
* Execute command on a remote host and wait for the command to complete.
*
* @param conn
* @param command
* @return CommandResults (contains stdout, stderr, and exit code)
* @throws IOException
* in several cases if there is a problem communicating with the remote host.
* @throws InterruptedException
* if execution of remote command is interrupted.
* @throws RemoteConnectionException
* if connection closed and cannot be opened.
* TODO: Expand to work with multiple platforms (assumes UNIX \n line endings.)
* TODO: Make robust against buffer overflows - use threads.
*/
public static CommandResults executeRemoteCommand(IRemoteConnection conn, String command, String remoteDirectory,
IProgressMonitor monitor) throws
IOException, InterruptedException, RemoteConnectionException {
if (!conn.isOpen()) {
try {
conn.open(monitor);
} catch (RemoteConnectionException e) {
throw e;
}
}
final CommandResults commandResults = new CommandResults();
// Run the command and wait for it to complete.
final List<String> commandStrings = Arrays.asList(command.split("\\s+")); //$NON-NLS-1$
final IRemoteProcessBuilder rpb = conn.getRemoteServices().getProcessBuilder(conn, commandStrings);
final IRemoteFileManager rfm = conn.getRemoteServices().getFileManager(conn);
if (rfm != null) {
rpb.directory(rfm.getResource(remoteDirectory));
}
IRemoteProcess rp = null;
try {
rp = rpb.start();
} catch (final IOException e) {
throw e;
}
// Read and store stdout and stderr.
BufferedReader commandOutputReader = null;
BufferedReader commandErrorReader = null;
commandOutputReader = new BufferedReader(new InputStreamReader(rp.getInputStream()));
commandErrorReader = new BufferedReader(new InputStreamReader(rp.getErrorStream()));
String output = ""; //$NON-NLS-1$
try {
String line;
while ((line = commandOutputReader.readLine()) != null) {
output += line;
output += "\n"; //$NON-NLS-1$
}
} catch (final IOException e) {
throw e;
} finally {
commandOutputReader.close();
}
String error = ""; //$NON-NLS-1$
try {
String line;
while ((line = commandErrorReader.readLine()) != null) {
error += line;
error += "\n"; //$NON-NLS-1$
}
} catch (final IOException e) {
throw e;
} finally {
commandErrorReader.close();
}
int exitCode;
try {
exitCode = rp.waitFor();
} catch (final InterruptedException e) {
throw e;
}
commandResults.setExitCode(exitCode);
commandResults.setStdout(output);
commandResults.setStderr(error);
return commandResults;
}
// Enforce as static
private CommandRunner() {
throw new AssertionError("Cannot create instances of static class CommandRunner"); //$NON-NLS-1$
}
}