/******************************************************************************* * Copyright (c) 2011 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: * Otavio Busatto Pontes <obusatto@br.ibm.com> - initial API and implementation * Red Hat - remote executable methods *******************************************************************************/ package org.eclipse.linuxtools.tools.launch.core.factory; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.linuxtools.profiling.launch.RemoteEnvProxyManager; import org.eclipse.linuxtools.tools.launch.core.properties.LinuxtoolsPathProperty; import com.jcraft.jsch.Channel; import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; /** * Abstract class with useful functions for ProcessFactory classes, * and statically-accessible helper functions for running remote processes. */ public abstract class LinuxtoolsProcessFactory { private static final int DEFAULT_PORT = 22; private static final String PATH = "PATH"; //$NON-NLS-1$ private static final String PATH_EQUAL = "PATH="; //$NON-NLS-1$ private static final String SEPARATOR = ":"; //$NON-NLS-1$ private String getEnvpPath(String[] envp) { if (envp == null) { return null; } for (String env : envp) { if (env.startsWith(PATH_EQUAL)) { return env.substring(PATH_EQUAL.length()); } } return null; } /** * Update the environment variables list with system environment variables * and the Linux tools path project property (if applicable), and prepend * the latter to the PATH env in the list. Call this function if the * command to be run may be in the path selected in 'Linux tools path' * project property page. * @param envp The list of new environment variables to use. * @param project If not <code>null</code>, only the environment of this project * will be updated. * @return The new environment. */ protected String[] updateEnvironment(String[] envp, IProject project) { String ltPath = LinuxtoolsPathProperty.getInstance().getLinuxtoolsPath(project); String envpPath = getEnvpPath(envp); Map<String, String> envMap = new HashMap<>(); if (project != null) { try { envMap.putAll(RemoteEnvProxyManager.class.newInstance().getEnv(project)); } catch (InstantiationException|IllegalAccessException|CoreException e) { e.printStackTrace(); } } String systemPath; if (!envMap.isEmpty()) { systemPath = envMap.get(PATH); if (systemPath == null) { systemPath = System.getenv(PATH); if (systemPath != null) { envMap.put(PATH, systemPath); } } } else { envMap.putAll(System.getenv()); systemPath = envMap.get(PATH); } StringBuffer newPath = new StringBuffer(); newPath.append(PATH_EQUAL); if (ltPath != null && !ltPath.isEmpty()) { newPath.append(ltPath); newPath.append(SEPARATOR); } if (envpPath != null && !envpPath.isEmpty()) { newPath.append(envpPath); newPath.append(SEPARATOR); } if (systemPath != null && !systemPath.isEmpty()) { newPath.append(systemPath); } // Overwrite/update map of system environment variables with ones from the provided array if (newPath.length() > PATH_EQUAL.length()) { envMap.put(PATH, newPath.substring(PATH_EQUAL.length())); } if (envp != null) { for (String var : envp) { if (!var.startsWith(PATH_EQUAL)) { int splitIndex = var.indexOf('='); envMap.put(var.substring(0, splitIndex), var.substring(splitIndex + 1)); } } } Set<String> keySet = envMap.keySet(); String[] newEnvp = new String[keySet.size()]; int i = 0; for (String key : keySet) { if (!key.startsWith(PATH_EQUAL)) { newEnvp[i] = key + "=" + envMap.get(key); //$NON-NLS-1$ } i++; } return newEnvp; } /** * Runs a command on the given host using the given credientials. * * @param args The command to run, followed by a list of optional arguments. * @param out A stream for the command's standard output. * @param err A stream for the command's standard error output. * @param user The user name to use on the remote machine. * @param host The host where the command will be run. * @param password The password for authenticating with the given host. * @return A {@link Channel} connected to the remotely running process. * @throws JSchException thrown if there are problems connecting to the remote machine. * @since 3.1 */ public static Channel execRemote(String[] args, OutputStream out, OutputStream err, String user, String host, String password) throws JSchException { return execRemote(args, out, err, user, host, password, DEFAULT_PORT, null); } /** * Runs a command on the given host using the given credentials. * * @param args The command to run, followed by a list of optional arguments. * @param out A stream for the command's standard output. * @param err A stream for the command's standard error output. * @param user the user name to use on the remote machine. * @param host the host where the command will be run. * @param password password for authenticating with the given host. * @param port The port to use during remote communication. * @param envp an array with extra enviroment variables to be used when running * the command. Set to <code>null</code> if none are needed. * @return a {@link Channel} connected to the remotely running process. * @throws JSchException thrown if there are problems connecting to the remote machine. * @since 3.1 */ public static Channel execRemote(String[] args, OutputStream out, OutputStream err, String user, String host, String password, int port, String[] envp) throws JSchException { JSch jsch = new JSch(); Session session = jsch.getSession(user, host, port); session.setPassword(password); Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); //$NON-NLS-1$//$NON-NLS-2$ session.setConfig(config); session.connect(); StringBuilder command = new StringBuilder(); if (envp != null) { for (String var : envp) { command.append(String.format("export %s; ", var)); //$NON-NLS-1$ } } for (int i = 0; i < args.length; i++) { command.append(args[i] + ' '); } ChannelExec channel = (ChannelExec) session.openChannel("exec"); //$NON-NLS-1$ channel.setPty(true); channel.setCommand(command.toString()); channel.setInputStream(null, true); channel.setOutputStream(out, true); channel.setExtOutputStream(err, true); channel.connect(); return channel; } /** * Runs a command on the given host using the given * credentials and waits for the process to finish executing, or until * the executing thread is interrupted. * * @param args The command to run, followed by a list of optional arguments. * @param out A stream for the command's standard output. * @param err A stream for the command's standard error output. * @param user the user name to use on the remote machine. * @param host the host where the command will be run. * @param password password for authenticating with the given host. * @return a {@link Channel} connected to the remotely running process. * @throws JSchException thrown if there are problems connecting to the remote machine. * @since 3.1 */ public static Channel execRemoteAndWait(String[] args, OutputStream out, OutputStream err, String user, String host, String password) throws JSchException { return execRemoteAndWait(args, out, err, user, host, password, DEFAULT_PORT, null); } /** * Runs a command on the given host using the given * credentials and waits for the process to finish executing, or until * the executing thread is interrupted. * * @param args The command to run, followed by a list of optional arguments. * @param out A stream for the command's standard output. * @param err A stream for the command's standard error output. * @param user the user name to use on the remote machine. * @param host the host where the command will be run. * @param password password for authenticating with the given host. * @param port The port to use during remote communication. * @param envp an array with extra enviroment variables to be used when running * the command. Set to <code>null</code> if none are needed. * @return a {@link Channel} connected to the remotely running process. * @throws JSchException thrown if there are problems connecting to the remote machine. * @since 3.1 */ public static Channel execRemoteAndWait(String[] args, OutputStream out, OutputStream err, String user, String host, String password, int port, String[] envp) throws JSchException { Channel channel = execRemote(args, out, err, user, host, password, port, envp); while (!channel.isClosed()) { try { Thread.sleep(250); } catch (InterruptedException e) { // Thread was interrupted just return. return channel; } } return channel; } /** * Convenience method: asks the channel to attempt termination of the * currently-running process on the channel's session. * @param channel The channel whose session process should be terminated. * @return <code>true</code> on a successul attempt of terminating the process, * <code>false</code> otherwise. * @since 3.2 */ public static boolean terminateProcess(Channel channel) { try { OutputStream out = channel.getOutputStream(); out.write(3); out.flush(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }