blob: 8967a686d42b45104c663662eae792384149dc2a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 Red Hat 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:
* Red Hat - Initial Contribution
*******************************************************************************/
package org.eclipse.cdt.internal.docker.launcher;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.docker.launcher.DockerLaunchUIPlugin;
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunchDelegate;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
import org.eclipse.linuxtools.docker.core.IDockerContainerInfo;
import org.eclipse.linuxtools.docker.core.IDockerNetworkSettings;
import org.eclipse.linuxtools.docker.ui.launch.ContainerLauncher;
import org.eclipse.linuxtools.docker.ui.launch.IContainerLaunchListener;
public class ContainerLaunchConfigurationDelegate extends GdbLaunchDelegate
implements ILaunchConfigurationDelegate {
private ContainerLauncher launcher;
private class StartGdbServerJob extends Job implements
IContainerLaunchListener {
private boolean started;
private boolean done;
private IDockerContainerInfo info;
public StartGdbServerJob(String name) {
super(name);
}
@Override
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask(getName(), IProgressMonitor.UNKNOWN);
while (!done) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
if (started && getIpAddress() != null)
done = true;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
monitor.done();
return Status.CANCEL_STATUS;
}
}
monitor.done();
return Status.OK_STATUS;
}
@Override
public void newOutput(String output) {
if (output.contains(Messages.Gdbserver_up)) {
started = true;
}
}
public String getIpAddress() {
if (info != null) {
IDockerNetworkSettings networkSettings = info.networkSettings();
return networkSettings.ipAddress();
}
return null;
}
public IDockerContainerInfo getContainerInfo() {
return info;
}
@Override
public void done() {
done = true;
}
@Override
public void containerInfo(IDockerContainerInfo info) {
this.info = info;
}
}
public ContainerLaunchConfigurationDelegate() {
super();
launcher = new ContainerLauncher();
}
@Override
public void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
IPath commandPath = getCommandPath(configuration);
if (commandPath != null) {
// create some labels to allow user to filter out such Containers if
// kept
Map<String, String> labels = new HashMap<>();
labels.put("CDTLaunch", ""); //$NON-NLS-1$ //$NON-NLS-2$
String projectName = configuration.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$
labels.put("CDTProject", projectName); //$NON-NLS-1$
if (mode.equals(ILaunchManager.RUN_MODE)) {
String commandDir = commandPath.removeLastSegments(1)
.toString();
StringBuilder b = new StringBuilder();
b.append(commandPath.toString().trim());
String arguments = getProgramArguments(configuration);
if (arguments.trim().length() > 0) {
b.append(" "); //$NON-NLS-1$
b.append(arguments);
}
String command = b.toString();
String workingDir = configuration
.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
(String) null);
Map<String, String> envMap = configuration.getAttribute(
ILaunchManager.ATTR_ENVIRONMENT_VARIABLES,
(Map<String, String>) null);
Map<String, String> origEnv = null;
boolean appendEnv = configuration
.getAttribute(
ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES,
false);
if (appendEnv) {
origEnv = System.getenv();
}
List<String> additionalDirs = configuration.getAttribute(
ILaunchConstants.ATTR_ADDITIONAL_DIRS,
(List<String>) null);
String image = configuration.getAttribute(
ILaunchConstants.ATTR_IMAGE, (String) null);
String connectionUri = configuration.getAttribute(
ILaunchConstants.ATTR_CONNECTION_URI, (String) "");
boolean keepContainer = configuration.getAttribute(
ILaunchConstants.ATTR_KEEP_AFTER_LAUNCH, false);
boolean supportStdin = configuration.getAttribute(
ILaunchConstants.ATTR_STDIN_SUPPORT, false);
boolean privilegedMode = configuration.getAttribute(
ILaunchConstants.ATTR_PRIVILEGED_MODE, false);
launcher.launch(DockerLaunchUIPlugin.PLUGIN_ID, null,
connectionUri,
image, command,
commandDir, workingDir, additionalDirs, origEnv,
envMap, null, keepContainer, supportStdin,
privilegedMode, labels);
} else if (mode.equals(ILaunchManager.DEBUG_MODE)) {
String gdbserverPortNumber = configuration.getAttribute(
ILaunchConstants.ATTR_GDBSERVER_PORT,
ILaunchConstants.ATTR_GDBSERVER_PORT_DEFAULT);
List<String> ports = Arrays
.asList(gdbserverPortNumber + "/tcp"); //$NON-NLS-1$
String gdbserverCommand = configuration.getAttribute(
ILaunchConstants.ATTR_GDBSERVER_COMMAND,
ILaunchConstants.ATTR_GDBSERVER_COMMAND_DEFAULT);
String commandArguments = ":" + gdbserverPortNumber + " " //$NON-NLS-1$ //$NON-NLS-2$
+ spaceEscapify(commandPath.toString());
String commandDir = commandPath.removeLastSegments(1)
.toString();
StringBuilder b = new StringBuilder();
b.append(gdbserverCommand).append(' ').append(commandArguments); //$NON-NLS-1$
String arguments = getProgramArguments(configuration);
if (arguments.trim().length() > 0) {
b.append(" "); //$NON-NLS-1$
b.append(arguments);
}
String command = b.toString();
String workingDir = configuration
.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_WORKING_DIRECTORY,
(String) null);
Map<String, String> envMap = configuration.getAttribute(
ILaunchManager.ATTR_ENVIRONMENT_VARIABLES,
(Map<String, String>) null);
Map<String, String> origEnv = null;
boolean appendEnv = configuration
.getAttribute(
ILaunchManager.ATTR_APPEND_ENVIRONMENT_VARIABLES,
false);
if (appendEnv) {
origEnv = System.getenv();
}
List<String> additionalDirs = configuration.getAttribute(
ILaunchConstants.ATTR_ADDITIONAL_DIRS,
(List<String>) null);
String image = configuration.getAttribute(
ILaunchConstants.ATTR_IMAGE, (String) null);
String connectionUri = configuration.getAttribute(
ILaunchConstants.ATTR_CONNECTION_URI, (String) "");
boolean keepContainer = configuration.getAttribute(
ILaunchConstants.ATTR_KEEP_AFTER_LAUNCH, false);
boolean supportStdin = configuration.getAttribute(
ILaunchConstants.ATTR_STDIN_SUPPORT, false);
boolean privilegedMode = configuration.getAttribute(
ILaunchConstants.ATTR_PRIVILEGED_MODE, false);
StartGdbServerJob job = new StartGdbServerJob(
Messages.Gdbserver_start);
job.schedule();
launcher.launch(DockerLaunchUIPlugin.PLUGIN_ID, job,
connectionUri,
image, command,
commandDir, workingDir, additionalDirs, origEnv,
envMap, ports, keepContainer, supportStdin,
privilegedMode, labels);
// wait until gdbserver is started successfully and we have its
// ip address or
// gdbserver has failed
try {
job.join();
} catch (InterruptedException e) {
// ignore
}
// if gdbserver started successfully and we have its ip address,
// launch the debugger
if (job.getResult() == Status.OK_STATUS
&& job.getIpAddress() != null) {
// Let debugger know how gdbserver was started on the remote
// container
ILaunchConfigurationWorkingCopy wc = configuration
.getWorkingCopy();
wc.setAttribute(
IGDBLaunchConfigurationConstants.ATTR_REMOTE_TCP,
true);
wc.setAttribute(
ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_START_MODE,
IGDBLaunchConfigurationConstants.DEBUGGER_MODE_REMOTE);
wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_HOST,
job.getIpAddress());
wc.setAttribute(IGDBLaunchConfigurationConstants.ATTR_PORT,
gdbserverPortNumber);
wc.doSave();
try {
super.launch(configuration, mode, launch, monitor);
} catch (CoreException ex) {
// launch failed, need to cleanup any container we
// created for the gdbserver
launcher.cleanup(connectionUri, job.getContainerInfo());
throw ex;
} finally {
monitor.done();
}
}
}
}
}
/**
* Get the program arguments and perform substitution.
*
* @param config
* launch configuration
* @return argument String
* @throws CoreException
*/
private String getProgramArguments(ILaunchConfiguration config)
throws CoreException {
String args = config.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS,
(String) "");
if (args != null && args.length() > 0) {
args = VariablesPlugin.getDefault().getStringVariableManager()
.performStringSubstitution(args);
}
return args;
}
/**
* Form command path using the project and program name.
*
* @param configuration
* @return command path
* @throws CoreException
*/
private IPath getCommandPath(ILaunchConfiguration configuration)
throws CoreException {
String projectName = configuration.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, "");
if (projectName.length() > 0) {
IProject project = CCorePlugin.getWorkspace().getRoot()
.getProject(projectName);
if (project == null)
return null;
String name = configuration.getAttribute(
ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME, "");
if (name.length() == 0)
return null;
IPath exePath = new Path(name);
if (!exePath.isAbsolute()) {
IPath location = project.getLocation();
if (location == null) {
return null;
}
exePath = location.append(name);
if (!exePath.toFile().exists()) {
// Try the old way, which is required to support linked
// resources.
IFile projFile = null;
try {
projFile = project.getFile(name);
} catch (IllegalArgumentException e) {
// thrown if relative path that resolves to a root file
// ("..\somefile")
}
if (projFile == null || !projFile.exists()) {
return null;
} else {
exePath = projFile.getLocation();
}
}
}
if (!exePath.toFile().exists()) {
return null;
}
if (!exePath.toFile().isFile()) {
return null;
}
return exePath;
} else {
return null;
}
}
private String spaceEscapify(String inputString) {
if (inputString == null)
return null;
return inputString.replaceAll(" ", "\\\\ "); //$NON-NLS-1$ //$NON-NLS-2$
}
@Override
protected String getPluginID() {
return DockerLaunchUIPlugin.PLUGIN_ID;
}
}