/********************************************************************************
* Copyright (c) 2009, 2010 MontaVista Software, Inc. and others.
* 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:
* Anna Dushistova (MontaVista) - initial API and implementation
* Anna Dushistova (Mentor Graphics) - [314659] moved common methods for DSF and CDI launches to this class
* Anna Dushistova (Mentor Graphics) - changed spaceEscapify visibility
********************************************************************************/
package org.eclipse.cdt.launch.remote;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.internal.launch.remote.Activator;
import org.eclipse.cdt.internal.launch.remote.Messages;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.osgi.util.NLS;
import org.eclipse.rse.core.RSECorePlugin;
import org.eclipse.rse.core.model.IHost;
import org.eclipse.rse.core.subsystems.ISubSystem;
import org.eclipse.rse.services.IService;
import org.eclipse.rse.services.clientserver.messages.SystemOperationCancelledException;
import org.eclipse.rse.services.files.IFileService;
import org.eclipse.rse.services.shells.HostShellProcessAdapter;
import org.eclipse.rse.services.shells.IHostShell;
import org.eclipse.rse.services.shells.IShellService;
import org.eclipse.rse.subsystems.files.core.servicesubsystem.IFileServiceSubSystem;
public class RSEHelper {
private final static String EXIT_CMD = "exit"; //$NON-NLS-1$
private final static String CMD_DELIMITER = ";"; //$NON-NLS-1$
public static IHost getRemoteConnectionByName(String remoteConnection) {
if (remoteConnection == null)
return null;
IHost[] connections = RSECorePlugin.getTheSystemRegistry().getHosts();
for (int i = 0; i < connections.length; i++)
if (connections[i].getAliasName().equals(remoteConnection))
return connections[i];
return null; // TODO Connection is not found in the list--need to react
// somehow, throw the exception?
}
public static IService getConnectedRemoteFileService(
IHost currentConnection, IProgressMonitor monitor) throws Exception {
final ISubSystem subsystem = getFileSubsystem(currentConnection);
if (subsystem == null)
throw new Exception(Messages.RemoteRunLaunchDelegate_4);
try {
subsystem.connect(monitor, false);
} catch (CoreException e) {
throw e;
} catch (OperationCanceledException e) {
throw new CoreException(Status.CANCEL_STATUS);
}
if (!subsystem.isConnected())
throw new Exception(Messages.RemoteRunLaunchDelegate_5);
return ((IFileServiceSubSystem) subsystem).getFileService();
}
public static IService getConnectedRemoteShellService(
IHost currentConnection, IProgressMonitor monitor) throws Exception {
ISubSystem subsystem = getSubSystemWithShellService(currentConnection);
if (subsystem != null) {
try {
subsystem.connect(monitor, false);
} catch (CoreException e) {
throw e;
} catch (OperationCanceledException e) {
throw new CoreException(Status.CANCEL_STATUS);
}
if (!subsystem.isConnected())
throw new Exception(Messages.RemoteRunLaunchDelegate_5);
return (IShellService) subsystem.getSubSystemConfiguration()
.getService(currentConnection).getAdapter(
IShellService.class);
} else {
throw new Exception(Messages.RemoteRunLaunchDelegate_4);
}
}
/**
* Find the first shell service associated with the host.
*
* @param host
* the connection
* @return shell service object, or <code>null</code> if not found.
*/
public static IShellService getShellService(IHost host) {
ISubSystem ss = getSubSystemWithShellService(host);
if (ss != null) {
return (IShellService) ss.getSubSystemConfiguration().getService(
host).getAdapter(IShellService.class);
}
return null;
}
/**
* Find the first IShellServiceSubSystem service associated with the host.
*
* @param host
* the connection
* @return shell service subsystem, or <code>null</code> if not found.
*/
public static ISubSystem getSubSystemWithShellService(IHost host) {
if (host == null)
return null;
ISubSystem[] subSystems = host.getSubSystems();
IShellService ssvc = null;
for (int i = 0; subSystems != null && i < subSystems.length; i++) {
IService svc = subSystems[i].getSubSystemConfiguration()
.getService(host);
if (svc != null) {
ssvc = (IShellService) svc.getAdapter(IShellService.class);
if (ssvc != null) {
return subSystems[i];
}
}
}
return null;
}
public static ISubSystem getFileSubsystem(IHost host) {
if (host == null)
return null;
ISubSystem[] subSystems = host.getSubSystems();
for (int i = 0; i < subSystems.length; i++) {
if (subSystems[i] instanceof IFileServiceSubSystem)
return subSystems[i];
}
return null;
}
public static IHost[] getSuitableConnections() {
ArrayList shellConnections = new ArrayList(Arrays.asList(RSECorePlugin.getTheSystemRegistry()
.getHostsBySubSystemConfigurationCategory("shells"))); //$NON-NLS-1$
ArrayList terminalConnections = new ArrayList(Arrays.asList(RSECorePlugin.getTheSystemRegistry()
.getHostsBySubSystemConfigurationCategory("terminals")));//$NON-NLS-1$
Iterator iter = terminalConnections.iterator();
while(iter.hasNext()){
Object terminalConnection = iter.next();
if(!shellConnections.contains(terminalConnection)){
shellConnections.add(terminalConnection);
}
}
return (IHost[]) shellConnections.toArray(new IHost[shellConnections.size()]);
}
public static void remoteFileDownload(ILaunchConfiguration config,
ILaunch launch, String localExePath, String remoteExePath,
IProgressMonitor monitor) throws CoreException {
boolean skipDownload = config
.getAttribute(
IRemoteConnectionConfigurationConstants.ATTR_SKIP_DOWNLOAD_TO_TARGET,
false);
if (skipDownload)
// Nothing to do. Download is skipped.
return;
monitor.beginTask(Messages.RemoteRunLaunchDelegate_2, 100);
IFileService fileService;
try {
fileService = (IFileService) RSEHelper
.getConnectedRemoteFileService(
getCurrentConnection(config),
new SubProgressMonitor(monitor, 10));
File file = new File(localExePath);
Path remotePath = new Path(remoteExePath);
fileService.upload(file, remotePath.removeLastSegments(1)
.toString(), remotePath.lastSegment(), true, null, null,
new SubProgressMonitor(monitor, 85));
// Need to change the permissions to match the original file
// permissions because of a bug in upload
remoteShellExec(
config,
"", "chmod", "+x " + spaceEscapify(remotePath.toString()), new SubProgressMonitor(monitor, 5)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} catch (SystemOperationCancelledException e) {
abort(e.getLocalizedMessage(), null, IStatus.CANCEL);
} catch (Exception e) {
abort(Messages.RemoteRunLaunchDelegate_6, e,
ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
} finally {
monitor.done();
}
}
public static String spaceEscapify(String inputString) {
if (inputString == null)
return null;
return inputString.replaceAll(" ", "\\\\ "); //$NON-NLS-1$ //$NON-NLS-2$
}
public static IHost getCurrentConnection(ILaunchConfiguration config)
throws CoreException {
String remoteConnection = config.getAttribute(
IRemoteConnectionConfigurationConstants.ATTR_REMOTE_CONNECTION,
""); //$NON-NLS-1$
IHost connection = RSEHelper
.getRemoteConnectionByName(remoteConnection);
if (connection == null) {
abort(Messages.RemoteRunLaunchDelegate_13, null,
ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
}
return connection;
}
public static Process remoteShellExec(ILaunchConfiguration config,
String prelaunchCmd, String remoteCommandPath, String arguments,
IProgressMonitor monitor) throws CoreException {
// The exit command is called to force the remote shell to close after
// our command
// is executed. This is to prevent a running process at the end of the
// debug session.
// See Bug 158786.
monitor.beginTask(NLS.bind(Messages.RemoteRunLaunchDelegate_8,
remoteCommandPath, arguments), 10);
String realRemoteCommand = arguments == null ? spaceEscapify(remoteCommandPath)
: spaceEscapify(remoteCommandPath) + " " + arguments; //$NON-NLS-1$
String remoteCommand = realRemoteCommand + CMD_DELIMITER + EXIT_CMD;
if (!prelaunchCmd.trim().equals("")) //$NON-NLS-1$
remoteCommand = prelaunchCmd + CMD_DELIMITER + remoteCommand;
IShellService shellService;
Process p = null;
try {
shellService = (IShellService) RSEHelper
.getConnectedRemoteShellService(
getCurrentConnection(config),
new SubProgressMonitor(monitor, 7));
// This is necessary because runCommand does not actually run the
// command right now.
String env[] = new String[0];
try {
IHostShell hostShell = shellService.launchShell(
"", env, new SubProgressMonitor(monitor, 3)); //$NON-NLS-1$
hostShell.writeToShell(remoteCommand);
p = new HostShellProcessAdapter(hostShell);
} catch (Exception e) {
if (p != null) {
p.destroy();
}
abort(Messages.RemoteRunLaunchDelegate_7, e,
ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
}
} catch (Exception e1) {
abort(e1.getMessage(), e1,
ICDTLaunchConfigurationConstants.ERR_INTERNAL_ERROR);
}
monitor.done();
return p;
}
public static String getRemoteHostname(ILaunchConfiguration config)
throws CoreException {
IHost currentConnection = getCurrentConnection(config);
return currentConnection.getHostName();
}
/**
* Throws a core exception with an error status object built from the given
* message, lower level exception, and error code.
*
* @param message
* the status message
* @param exception
* lower level exception associated with the error, or
* <code>null</code> if none
* @param code
* error code
*/
public static void abort(String message, Throwable exception, int code) throws CoreException {
IStatus status;
if (exception != null) {
MultiStatus multiStatus = new MultiStatus(Activator.PLUGIN_ID, code, message, exception);
multiStatus.add(new Status(IStatus.ERROR, Activator.PLUGIN_ID, code, exception.getLocalizedMessage(), exception));
status= multiStatus;
} else {
status= new Status(IStatus.ERROR, Activator.PLUGIN_ID, code, message, null);
}
throw new CoreException(status);
}
}