| package org.eclipse.cdt.docker.launcher; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| |
| import org.eclipse.cdt.core.ICommandLauncher; |
| import org.eclipse.cdt.core.model.CoreModel; |
| import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; |
| import org.eclipse.cdt.internal.core.ProcessClosure; |
| import org.eclipse.cdt.internal.docker.launcher.Messages; |
| import org.eclipse.cdt.internal.docker.launcher.PreferenceConstants; |
| import org.eclipse.cdt.managedbuilder.buildproperties.IOptionalBuildProperties; |
| import org.eclipse.cdt.managedbuilder.core.IConfiguration; |
| import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager; |
| 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.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.core.variables.VariablesPlugin; |
| import org.eclipse.linuxtools.docker.ui.launch.ContainerLauncher; |
| import org.eclipse.linuxtools.docker.ui.launch.IErrorMessageHolder; |
| import org.eclipse.linuxtools.internal.docker.ui.launch.ContainerCommandProcess; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.service.prefs.Preferences; |
| |
| @SuppressWarnings("restriction") |
| public class ContainerCommandLauncher |
| implements ICommandLauncher, IErrorMessageHolder { |
| |
| public final static String CONTAINER_BUILD_ENABLED = DockerLaunchUIPlugin.PLUGIN_ID |
| + ".containerbuild.property.enablement"; //$NON-NLS-1$ |
| public final static String CONNECTION_ID = DockerLaunchUIPlugin.PLUGIN_ID |
| + ".containerbuild.property.connection"; //$NON-NLS-1$ |
| public final static String IMAGE_ID = DockerLaunchUIPlugin.PLUGIN_ID |
| + ".containerbuild.property.image"; //$NON-NLS-1$ |
| public final static String VOLUMES_ID = DockerLaunchUIPlugin.PLUGIN_ID |
| + ".containerbuild.property.volumes"; //$NON-NLS-1$ |
| public final static String SELECTED_VOLUMES_ID = DockerLaunchUIPlugin.PLUGIN_ID |
| + ".containerbuild.property.selectedvolumes"; //$NON-NLS-1$ |
| |
| public final static String VOLUME_SEPARATOR_REGEX = "[|]"; //$NON-NLS-1$ |
| |
| private IProject fProject; |
| private Process fProcess; |
| private boolean fShowCommand; |
| private String fErrorMessage; |
| private Properties fEnvironment; |
| |
| private String[] commandArgs; |
| private String fImageName = ""; //$NON-NLS-1$ |
| |
| public final static int COMMAND_CANCELED = ICommandLauncher.COMMAND_CANCELED; |
| public final static int ILLEGAL_COMMAND = ICommandLauncher.ILLEGAL_COMMAND; |
| public final static int OK = ICommandLauncher.OK; |
| |
| private static final String NEWLINE = System.getProperty("line.separator", //$NON-NLS-1$ |
| "\n"); //$NON-NLS-1$ |
| |
| /** |
| * The number of milliseconds to pause between polling. |
| */ |
| protected static final long DELAY = 50L; |
| |
| @Override |
| public void setProject(IProject project) { |
| this.fProject = project; |
| } |
| |
| @Override |
| public IProject getProject() { |
| return fProject; |
| } |
| |
| @SuppressWarnings("unused") |
| private String getImageName() { |
| return fImageName; |
| } |
| |
| private void setImageName(String imageName) { |
| fImageName = imageName; |
| } |
| |
| @Override |
| public void showCommand(boolean show) { |
| this.fShowCommand = show; |
| } |
| |
| @Override |
| public String getErrorMessage() { |
| return fErrorMessage; |
| } |
| |
| @Override |
| public void setErrorMessage(String error) { |
| fErrorMessage = error; |
| } |
| |
| @Override |
| public String[] getCommandArgs() { |
| return commandArgs; |
| } |
| |
| @Override |
| public Properties getEnvironment() { |
| return fEnvironment; |
| } |
| |
| @Override |
| public String getCommandLine() { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public Process execute(IPath commandPath, String[] args, String[] env, |
| IPath workingDirectory, IProgressMonitor monitor) |
| throws CoreException { |
| |
| HashMap<String, String> labels = new HashMap<>(); |
| labels.put("org.eclipse.cdt.container-command", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| String projectName = fProject.getName(); |
| labels.put("org.eclipse.cdt.project-name", projectName); //$NON-NLS-1$ |
| |
| List<String> additionalDirs = new ArrayList<>(); |
| |
| // |
| IPath projectLocation = fProject.getLocation(); |
| String projectPath = projectLocation.toPortableString(); |
| if (projectLocation.getDevice() != null) { |
| projectPath = "/" + projectPath.replace(':', '/'); //$NON-NLS-1$ |
| } |
| additionalDirs.add(projectPath); |
| |
| ArrayList<String> commandSegments = new ArrayList<>(); |
| |
| StringBuilder b = new StringBuilder(); |
| String commandString = commandPath.toPortableString(); |
| if (commandPath.getDevice() != null) { |
| commandString = "/" + commandString.replace(':', '/'); //$NON-NLS-1$ |
| } |
| b.append(commandString); |
| commandSegments.add(commandString); |
| for (String arg : args) { |
| b.append(" "); //$NON-NLS-1$ |
| String realArg = VariablesPlugin.getDefault() |
| .getStringVariableManager().performStringSubstitution(arg); |
| if (Platform.getOS().equals(Platform.OS_WIN32)) { |
| // check if file exists and if so, add an additional directory |
| IPath p = new Path(realArg); |
| if (p.isValidPath(realArg) && p.getDevice() != null) { |
| File f = p.toFile(); |
| String modifiedArg = realArg; |
| // if the directory of the arg as a file exists, we mount it |
| // and modify the argument to be unix-style |
| if (f.isFile()) { |
| f = f.getParentFile(); |
| modifiedArg = "/" //$NON-NLS-1$ |
| + p.toPortableString().replace(':', '/'); |
| p = p.removeLastSegments(1); |
| } |
| if (f != null && f.exists()) { |
| additionalDirs.add( |
| "/" + p.toPortableString().replace(':', '/')); //$NON-NLS-1$ |
| realArg = modifiedArg; |
| } |
| } |
| } else if (realArg.startsWith("/")) { //$NON-NLS-1$ |
| // check if file directory exists and if so, add an additional |
| // directory |
| IPath p = new Path(realArg); |
| if (p.isValidPath(realArg)) { |
| File f = p.toFile(); |
| if (f.isFile()) { |
| f = f.getParentFile(); |
| } |
| if (f != null && f.exists()) { |
| additionalDirs.add(f.getAbsolutePath()); |
| } |
| } |
| } |
| b.append(realArg); |
| commandSegments.add(realArg); |
| } |
| |
| commandArgs = commandSegments.toArray(new String[0]); |
| |
| String commandDir = commandPath.removeLastSegments(1).toString(); |
| if (commandDir.isEmpty()) { |
| commandDir = null; |
| } else if (commandPath.getDevice() != null) { |
| commandDir = "/" + commandDir.replace(':', '/'); //$NON-NLS-1$ |
| } |
| |
| IProject[] referencedProjects = fProject.getReferencedProjects(); |
| for (IProject referencedProject : referencedProjects) { |
| String referencedProjectPath = referencedProject.getLocation() |
| .toPortableString(); |
| if (referencedProject.getLocation().getDevice() != null) { |
| referencedProjectPath = "/" //$NON-NLS-1$ |
| + referencedProjectPath.replace(':', '/'); |
| } |
| additionalDirs |
| .add(referencedProjectPath); |
| } |
| |
| String command = b.toString(); |
| |
| String workingDir = workingDirectory.makeAbsolute().toPortableString(); |
| if (workingDirectory.toPortableString().equals(".")) { //$NON-NLS-1$ |
| workingDir = "/tmp"; //$NON-NLS-1$ |
| } else if (workingDirectory.getDevice() != null) { |
| workingDir = "/" + workingDir.replace(':', '/'); //$NON-NLS-1$ |
| } |
| parseEnvironment(env); |
| Map<String, String> origEnv = null; |
| |
| boolean supportStdin = false; |
| |
| boolean privilegedMode = false; |
| |
| ContainerLauncher launcher = new ContainerLauncher(); |
| |
| Preferences prefs = InstanceScope.INSTANCE |
| .getNode(DockerLaunchUIPlugin.PLUGIN_ID); |
| |
| boolean keepContainer = prefs.getBoolean( |
| PreferenceConstants.KEEP_CONTAINER_AFTER_LAUNCH, false); |
| |
| ICConfigurationDescription cfgd = CoreModel.getDefault() |
| .getProjectDescription(fProject).getActiveConfiguration(); |
| IConfiguration cfg = ManagedBuildManager |
| .getConfigurationForDescription(cfgd); |
| if (cfg == null) { |
| return null; |
| } |
| IOptionalBuildProperties props = cfg.getOptionalBuildProperties(); |
| |
| // Add any specified volumes to additional dir list |
| String selectedVolumeString = props.getProperty(SELECTED_VOLUMES_ID); |
| if (selectedVolumeString != null && !selectedVolumeString.isEmpty()) { |
| String[] selectedVolumes = selectedVolumeString |
| .split(VOLUME_SEPARATOR_REGEX); |
| if (Platform.getOS().equals(Platform.OS_WIN32)) { |
| for (String selectedVolume : selectedVolumes) { |
| IPath path = new Path(selectedVolume); |
| String selectedPath = path.toPortableString(); |
| if (path.getDevice() != null) { |
| selectedPath = "/" + selectedPath.replace(':', '/'); //$NON-NLS-1$ |
| } |
| additionalDirs.add(selectedPath); |
| } |
| } else { |
| additionalDirs.addAll(Arrays.asList(selectedVolumes)); |
| } |
| } |
| |
| String connectionName = props |
| .getProperty(ContainerCommandLauncher.CONNECTION_ID); |
| if (connectionName == null) { |
| return null; |
| } |
| String imageName = props |
| .getProperty(ContainerCommandLauncher.IMAGE_ID); |
| if (imageName == null) { |
| return null; |
| } |
| setImageName(imageName); |
| |
| fProcess = launcher.runCommand(connectionName, imageName, fProject, |
| this, |
| command, |
| commandDir, |
| workingDir, |
| additionalDirs, |
| origEnv, fEnvironment, supportStdin, privilegedMode, |
| labels, keepContainer); |
| |
| return fProcess; |
| } |
| |
| /** |
| * Parse array of "ENV=value" pairs to Properties. |
| */ |
| private void parseEnvironment(String[] env) { |
| fEnvironment = null; |
| if (env != null) { |
| fEnvironment = new Properties(); |
| for (String envStr : env) { |
| // Split "ENV=value" and put in Properties |
| int pos = envStr.indexOf('='); // $NON-NLS-1$ |
| if (pos < 0) |
| pos = envStr.length(); |
| String key = envStr.substring(0, pos); |
| String value = envStr.substring(pos + 1); |
| fEnvironment.put(key, value); |
| } |
| } |
| } |
| |
| @Override |
| public int waitAndRead(OutputStream out, OutputStream err) { |
| printImageHeader(out); |
| |
| if (fShowCommand) { |
| printCommandLine(out); |
| } |
| |
| if (fProcess == null) { |
| return ILLEGAL_COMMAND; |
| } |
| ProcessClosure closure = new ProcessClosure(fProcess, out, err); |
| closure.runBlocking(); // a blocking call |
| return OK; |
| } |
| |
| @Override |
| public int waitAndRead(OutputStream output, OutputStream err, |
| IProgressMonitor monitor) { |
| printImageHeader(output); |
| |
| if (fShowCommand) { |
| printCommandLine(output); |
| } |
| |
| if (fProcess == null) { |
| return ILLEGAL_COMMAND; |
| } |
| |
| ProcessClosure closure = new ProcessClosure(fProcess, output, err); |
| closure.runNonBlocking(); |
| Runnable watchProcess = () -> { |
| try { |
| fProcess.waitFor(); |
| } catch (InterruptedException e) { |
| // ignore |
| } |
| closure.terminate(); |
| }; |
| Thread t = new Thread(watchProcess); |
| t.start(); |
| while (!monitor.isCanceled() && closure.isAlive()) { |
| try { |
| Thread.sleep(DELAY); |
| } catch (InterruptedException ie) { |
| break; |
| } |
| } |
| try { |
| t.join(500); |
| } catch (InterruptedException e1) { |
| // ignore |
| } |
| int state = OK; |
| |
| // Operation canceled by the user, terminate abnormally. |
| if (monitor.isCanceled()) { |
| closure.terminate(); |
| state = COMMAND_CANCELED; |
| setErrorMessage(Messages.CommandLauncher_CommandCancelled); |
| } |
| try { |
| fProcess.waitFor(); |
| } catch (InterruptedException e) { |
| // ignore |
| } |
| |
| monitor.done(); |
| return state; |
| } |
| |
| protected void printImageHeader(OutputStream os) { |
| if (os != null) { |
| try { |
| os.write(NLS |
| .bind(Messages.ContainerCommandLauncher_image_msg, |
| ((ContainerCommandProcess) fProcess).getImage()) |
| .getBytes()); |
| os.write(NEWLINE.getBytes()); |
| os.flush(); |
| } catch (IOException e) { |
| // ignore |
| } |
| } |
| } |
| |
| protected void printCommandLine(OutputStream os) { |
| if (os != null) { |
| try { |
| os.write(getCommandLineQuoted(getCommandArgs(), true) |
| .getBytes()); |
| os.flush(); |
| } catch (IOException e) { |
| // ignore; |
| } |
| } |
| } |
| |
| @SuppressWarnings("nls") |
| private String getCommandLineQuoted(String[] commandArgs, boolean quote) { |
| StringBuilder buf = new StringBuilder(); |
| if (commandArgs != null) { |
| for (String commandArg : commandArgs) { |
| if (quote && (commandArg.contains(" ") |
| || commandArg.contains("\"") |
| || commandArg.contains("\\"))) { |
| commandArg = '"' + commandArg.replaceAll("\\\\", "\\\\\\\\") |
| .replaceAll("\"", "\\\\\"") + '"'; |
| } |
| buf.append(commandArg); |
| buf.append(' '); |
| } |
| buf.append(NEWLINE); |
| } |
| return buf.toString(); |
| } |
| |
| protected String getCommandLine(String[] commandArgs) { |
| return getCommandLineQuoted(commandArgs, false); |
| } |
| |
| } |