From 41ac47d732eed8392d60d0f6773e5a279d49b999 Mon Sep 17 00:00:00 2001 From: Adrian Dudau Date: Thu, 12 Dec 2013 13:36:50 +0100 Subject: initial commit of Enea Linux 3.1 Migrated from the internal git server on the dora-enea branch Signed-off-by: Adrian Dudau --- .../src/org/yocto/remote/utils/Activator.java | 62 ++++ .../src/org/yocto/remote/utils/CommonHelper.java | 46 +++ .../src/org/yocto/remote/utils/DialogRunnable.java | 26 ++ .../src/org/yocto/remote/utils/Messages.java | 35 +++ .../src/org/yocto/remote/utils/RSEHelper.java | 344 +++++++++++++++++++++ .../org/yocto/remote/utils/RemoteShellExec.java | 140 +++++++++ .../src/org/yocto/remote/utils/ShellSession.java | 332 ++++++++++++++++++++ .../org/yocto/remote/utils/TerminalHandler.java | 132 ++++++++ 8 files changed, 1117 insertions(+) create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Activator.java create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/CommonHelper.java create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/DialogRunnable.java create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Messages.java create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RSEHelper.java create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RemoteShellExec.java create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/ShellSession.java create mode 100644 plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/TerminalHandler.java (limited to 'plugins/org.yocto.remote.utils/src/org/yocto/remote/utils') diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Activator.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Activator.java new file mode 100644 index 0000000..da66a3e --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Activator.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2013 Intel Corporation. + * 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: + * Intel - initial API and implementation + *******************************************************************************/ +package org.yocto.remote.utils; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.yocto.remote.utils"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/CommonHelper.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/CommonHelper.java new file mode 100644 index 0000000..23afd38 --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/CommonHelper.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2013 Intel Corporation. + * 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: + * Intel - initial API and implementation + *******************************************************************************/ +package org.yocto.remote.utils; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.swt.widgets.Display; + +public class CommonHelper { + + static public boolean isExecAvail(String exec) { + boolean ret = false; + try { + Process p = Runtime.getRuntime().exec(new String[] {"which", exec}); + p.waitFor(); + if(p.exitValue() == 0) { + ret = true; + } + } catch (Exception e) { + e.printStackTrace(); + } + return ret; + } + + public static void showErrorDialog(final String dialogTitle, final String errorMessage, final String reason) { + //needs to be run in the ui thread otherwise swt throws invalid thread access + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + ErrorDialog.openError(null, dialogTitle, errorMessage, + new Status(IStatus.ERROR,Activator.PLUGIN_ID,reason)); + } + }); + + } + +} diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/DialogRunnable.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/DialogRunnable.java new file mode 100644 index 0000000..5fbab76 --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/DialogRunnable.java @@ -0,0 +1,26 @@ +package org.yocto.remote.utils; + +import org.eclipse.jface.dialogs.MessageDialog; + +public class DialogRunnable implements Runnable{ + int type = 0; + boolean result; + public static final int QUESTION = 1; + public static final int ERROR = 2; + String title; + String message; + + DialogRunnable(String title, String message, int type){ + this.title = title; + this.message = message; + this.type = type; + } + @Override + public void run() { + if (type == QUESTION) { + result = MessageDialog.openQuestion(null, title, message); + } else if (type == ERROR) { + MessageDialog.openError(null, title, message); + } + } +} \ No newline at end of file diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Messages.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Messages.java new file mode 100644 index 0000000..fc696d6 --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/Messages.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2013 Intel Corporation. + * 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: + * Intel - initial API and implementation + *******************************************************************************/ +package org.yocto.remote.utils; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.yocto.remote.utils.messages"; //$NON-NLS-1$ + + public static String ErrorNoSubsystem; + public static String ErrorConnectSubsystem; + + public static String InfoDownload; + public static String InfoUpload; + + public static String RemoteShellExec_1; + public static String RemoteShellExec_2; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RSEHelper.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RSEHelper.java new file mode 100644 index 0000000..201c944 --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RSEHelper.java @@ -0,0 +1,344 @@ +/******************************************************************************** + * Copyright (c) 2013 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 + * Lianhao Lu (Intel) - Modified to add other file operations. + * Ioana Grigoropol (Intel) - Separated remote functionality + ********************************************************************************/ +package org.yocto.remote.utils; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +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.osgi.util.NLS; +import org.eclipse.rse.core.IRSECoreStatusCodes; +import org.eclipse.rse.core.IRSESystemType; +import org.eclipse.rse.core.RSECorePlugin; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.core.model.ISubSystemConfigurationCategories; +import org.eclipse.rse.core.model.ISystemRegistry; +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.services.IService; +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; +import org.eclipse.rse.subsystems.shells.core.subsystems.servicesubsystem.IShellServiceSubSystem; +import org.eclipse.rse.subsystems.terminals.core.ITerminalServiceSubSystem; + +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.ErrorNoSubsystem); + + 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.ErrorConnectSubsystem); + + return ((IFileServiceSubSystem) subsystem).getFileService(); + } + + 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 IService getConnectedShellService( + IHost currentConnection, IProgressMonitor monitor) throws Exception { + final ISubSystem subsystem = getShellSubsystem(currentConnection); + + if (subsystem == null) + throw new Exception(Messages.ErrorNoSubsystem); + + 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.ErrorConnectSubsystem); + + return ((IShellServiceSubSystem) subsystem).getShellService(); + } + + public static ISubSystem getShellSubsystem(IHost host) { + if (host == null) + return null; + ISubSystem[] subSystems = host.getSubSystems(); + for (int i = 0; i < subSystems.length; i++) { + if (subSystems[i] instanceof IShellServiceSubSystem) + return subSystems[i]; + } + return null; + } + + public static IHost[] getSuitableConnections() { + + //we only get RSE connections with files&cmds subsystem + ArrayList filConnections = new ArrayList (Arrays.asList(RSECorePlugin.getTheSystemRegistry() + .getHostsBySubSystemConfigurationCategory(ISubSystemConfigurationCategories.SUBSYSTEM_CATEGORY_FILES))); //$NON-NLS-1$ + + ArrayList terminalConnections = new ArrayList (Arrays.asList(RSECorePlugin.getTheSystemRegistry() + .getHostsBySubSystemConfigurationCategory("terminal")));//$NON-NLS-1$ + + ArrayList shellConnections = new ArrayList (Arrays.asList(RSECorePlugin.getTheSystemRegistry() + .getHostsBySubSystemConfigurationCategory("shells"))); //$NON-NLS-1$ + + Iterator iter = filConnections.iterator(); + while(iter.hasNext()){ + IHost fileConnection = iter.next(); + if(!terminalConnections.contains(fileConnection) && !shellConnections.contains(fileConnection)){ + iter.remove(); + } + IRSESystemType sysType = fileConnection.getSystemType(); + if (sysType == null || !sysType.isEnabled()) { + iter.remove(); + } + } + + return filConnections.toArray(new IHost[filConnections.size()]); + } + + public static void putRemoteFileInPlugin(IHost connection, String locaPathInPlugin, String remoteExePath, + IProgressMonitor monitor) throws Exception { + + assert(connection != null); + monitor.beginTask(Messages.InfoUpload, 100); + + IFileService fileService; + try { + fileService = (IFileService) getConnectedRemoteFileService( + connection, + new SubProgressMonitor(monitor, 5)); + InputStream inputStream = FileLocator.openStream( + Activator.getDefault().getBundle(), new Path(locaPathInPlugin), false); + Path remotePath = new Path(remoteExePath); + + //TODO workaround for now + //in case the underlying scp file service doesn't support inputStream upload + BufferedInputStream bis = new BufferedInputStream(inputStream); + File tempFile = File.createTempFile("scp", "temp"); //$NON-NLS-1$ //$NON-NLS-2$ + FileOutputStream os = new FileOutputStream(tempFile); + BufferedOutputStream bos = new BufferedOutputStream(os); + byte[] buffer = new byte[1024]; + int readCount; + while( (readCount = bis.read(buffer)) > 0) + { + bos.write(buffer, 0, readCount); + } + bos.close(); + fileService.upload(tempFile, remotePath.removeLastSegments(1) + .toString(), remotePath.lastSegment(), true, null, null, + new SubProgressMonitor(monitor, 80)); + // Need to change the permissions to match the original file + // permissions because of a bug in upload + remoteShellExec( + connection, + "", "chmod", "+x " + spaceEscapify(remotePath.toString()), new SubProgressMonitor(monitor, 5)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + } finally { + monitor.done(); + } + return; + } + + public static void getRemoteFile(IHost connection, String localExePath, String remoteExePath, + IProgressMonitor monitor) throws Exception { + + assert(connection!=null); + monitor.beginTask(Messages.InfoDownload, 100); + + IFileService fileService; + try { + fileService = (IFileService) getConnectedRemoteFileService( + connection, + new SubProgressMonitor(monitor, 10)); + File file = new File(localExePath); + file.deleteOnExit(); + monitor.worked(5); + Path remotePath = new Path(remoteExePath); + fileService.download(remotePath.removeLastSegments(1).toString(), + remotePath.lastSegment(),file,true, null, + new SubProgressMonitor(monitor, 85)); + // Need to change the permissions to match the original file + // permissions because of a bug in upload + //RemoteApplication p = remoteShellExec( + // config, + // "", "chmod", "+x " + spaceEscapify(remotePath.toString()), new SubProgressMonitor(monitor, 5)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + //Thread.sleep(500); + //p.destroy(); + + } finally { + monitor.done(); + } + return; + } + + public static ITerminalServiceSubSystem getTerminalSubSystem( + IHost connection) { + ISystemRegistry systemRegistry = RSECorePlugin.getTheSystemRegistry(); + ISubSystem[] subsystems = systemRegistry.getSubSystems(connection); + for (int i = 0; i < subsystems.length; i++) { + if (subsystems[i] instanceof ITerminalServiceSubSystem) { + ITerminalServiceSubSystem subSystem = (ITerminalServiceSubSystem) subsystems[i]; + return subSystem; + } + } + return null; + } + + public static String spaceEscapify(String inputString) { + if (inputString == null) + return null; + + return inputString.replaceAll(" ", "\\\\ "); //$NON-NLS-1$ //$NON-NLS-2$ + } + + public static Process remoteShellExec(IHost connection, + String prelaunchCmd, String remoteCommandPath, String arguments, + IProgressMonitor monitor) throws CoreException { + + monitor.beginTask(NLS.bind(Messages.RemoteShellExec_1, + remoteCommandPath, arguments), 10); + String realRemoteCommand = arguments == null ? spaceEscapify(remoteCommandPath) + : spaceEscapify(remoteCommandPath) + " " + arguments; //$NON-NLS-1$ + + String remoteCommand = realRemoteCommand + CMD_DELIMITER + EXIT_CMD; + + if(prelaunchCmd != null) { + if (!prelaunchCmd.trim().equals("")) //$NON-NLS-1$ + remoteCommand = prelaunchCmd + CMD_DELIMITER + remoteCommand; + } + + IShellService shellService; + Process p = null; + try { + shellService = (IShellService) getConnectedShellService( + connection, + 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.RemoteShellExec_2, e, + IRSECoreStatusCodes.EXCEPTION_OCCURRED); + } + } catch (Exception e1) { + abort(e1.getMessage(), e1, + IRSECoreStatusCodes.EXCEPTION_OCCURRED); + } + + monitor.done(); + return p; + } + + /** + * 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 + * null 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); + } + /** + * Checks whether a IHost associated system's is enabled and not a local one + * @param host + * @return + */ + public static boolean isHostViable(IHost host) { + IRSESystemType sysType = host.getSystemType(); + if (sysType != null && sysType.isEnabled() && !sysType.isLocal()) + return true; + return false; + } + + /** + * Ensures that RSECorePlugin is initialized before performing any actions + */ + public static void waitForRSEInitCompletition() { + if (!RSECorePlugin.isInitComplete(RSECorePlugin.INIT_MODEL)) + try { + RSECorePlugin.waitForInitCompletion(RSECorePlugin.INIT_MODEL); + } catch (InterruptedException e) { + return; + } + } +} diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RemoteShellExec.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RemoteShellExec.java new file mode 100644 index 0000000..a7fe221 --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/RemoteShellExec.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2013 Intel Corporation. + * 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: + * Intel - initial API and implementation + *******************************************************************************/ +package org.yocto.remote.utils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.rse.core.model.IHost; + +public class RemoteShellExec { + + public static final int + STATE_NULL = 0, + STATE_RUNNING = 1, + STATE_EXITED = 2; + + private final String command; + private final IHost host; + + private InputStream fInStream; + private OutputStream fOutStream; + private InputStream fErrStream; + private Process remoteShellProcess; + + private int exitCode = 0; + private int status = STATE_NULL; + + private final String RETURN_VALUE_TAG = "org.yocto.sdk.remotetools.RVTAG"; + private final String RETURN_VALUE_CMD = ";echo \"" + RETURN_VALUE_TAG + "$?\""; + + public RemoteShellExec(IHost host, String command) { + assert(host != null); + this.host = host; + this.command = command; + } + + public int getStatus() { + return status; + } + + public int getExitCode() { + return exitCode; + } + + private void reset() { + fInStream = null; + fOutStream = null; + fErrStream = null; + + remoteShellProcess = null; + exitCode = 0; + status = STATE_NULL; + } + + public InputStream getInputStream() { + return fInStream; + } + + public OutputStream getOutputStream() { + return fOutStream; + } + + public InputStream getErrStream() { + return fErrStream; + } + + public synchronized void start(String prelaunchCmd, String argument, IProgressMonitor monitor) throws Exception { + if(status == STATE_RUNNING) + return; + + reset(); + argument = (argument == null ? RETURN_VALUE_CMD : argument + RETURN_VALUE_CMD); + remoteShellProcess = RSEHelper.remoteShellExec(this.host, prelaunchCmd, this.command, argument, monitor); + fInStream = remoteShellProcess.getInputStream(); + fOutStream = remoteShellProcess.getOutputStream(); + fErrStream = remoteShellProcess.getErrorStream(); + status = STATE_RUNNING; + } + + public synchronized void terminate() throws Exception { + if(status != STATE_RUNNING || remoteShellProcess != null) + return; + + remoteShellProcess.destroy(); + reset(); + } + + public int waitFor(IProgressMonitor monitor) throws InterruptedException { + while(status == STATE_RUNNING) { + if(monitor != null) { + if(monitor.isCanceled()) { + throw new InterruptedException("User Cancelled"); + } + } + + try { + remoteShellProcess.waitFor(); + }catch(InterruptedException e){ + //get the return value + try { + if(fInStream.available() != 0) { + BufferedReader in = new BufferedReader(new InputStreamReader(fInStream)); + String thisline; + int idx; + while((thisline = in.readLine()) != null) { + if(thisline.indexOf(RETURN_VALUE_CMD) == -1) { + idx = thisline.indexOf(RETURN_VALUE_TAG); + if(idx != -1) { + try { + exitCode=(new Integer(thisline.substring(idx+RETURN_VALUE_TAG.length()))).intValue(); + }catch(NumberFormatException e2) { + } + break; + } + } + } + } + }catch(IOException e1) { + //do nothing + } + }finally { + status=STATE_EXITED; + } + } + return exitCode; + } +} + diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/ShellSession.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/ShellSession.java new file mode 100644 index 0000000..0922824 --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/ShellSession.java @@ -0,0 +1,332 @@ +/***************************************************************************** + * Copyright (c) 2013 Ken Gilmer + * 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: + * Ken Gilmer - initial API and implementation + * Jessica Zhang - Adopt for Yocto Tools plugin + *******************************************************************************/ +package org.yocto.remote.utils; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.lang.reflect.InvocationTargetException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.swt.widgets.Display; + +public class ShellSession { + /** + * Bash shell + */ + public static final int SHELL_TYPE_BASH = 1; + /** + * sh shell + */ + public static final int SHELL_TYPE_SH = 2; + private volatile boolean interrupt = false; + /** + * String used to isolate command execution + */ + public static final String TERMINATOR = "build$"; + public static final String LT = System.getProperty("line.separator"); + private Process process; + + private OutputStream pos = null; + + private String shellPath = null; + private final String initCmd; + private final File root; + private final File builddir; + + private OutputStreamWriter out; + + public static String getFilePath(String file) throws IOException { + File f = new File(file); + + if (!f.exists() || f.isDirectory()) { + throw new IOException("Path passed is not a file: " + file); + } + + StringBuffer sb = new StringBuffer(); + + String elems[] = file.split(File.separator); + + for (int i = 0; i < elems.length - 1; ++i) { + sb.append(elems[i]); + sb.append(File.separator); + } + + return sb.toString(); + } + + public ShellSession(int shellType, File root, File builddir, String initCmd, OutputStream out) throws IOException { + this.root = root; + this.builddir = builddir; + this.initCmd = initCmd; + if (out == null) { + this.out = new OutputStreamWriter(null); + } else { + this.out = new OutputStreamWriter(out); + } + if (shellType == SHELL_TYPE_SH) { + shellPath = "/bin/sh"; + } + shellPath = "/bin/bash"; + + initializeShell(); + } + + private void initializeShell() throws IOException { + process = Runtime.getRuntime().exec(shellPath); + pos = process.getOutputStream(); + + if (root != null) { + execute("cd " + root.getAbsolutePath()); + } + + if (initCmd != null) { + execute("source " + initCmd + " " + builddir.getAbsolutePath()); + } + } + + synchronized + public String execute(String command, int[] retCode) throws IOException { + String errorMessage = null; + + interrupt = false; + out.write(command); + out.write(LT); + + sendToProcessAndTerminate(command); + + if (process.getErrorStream().available() > 0) { + byte[] msg = new byte[process.getErrorStream().available()]; + + process.getErrorStream().read(msg, 0, msg.length); + String msg_str = new String(msg); + out.write(msg_str); + out.write(LT); + if (!msg_str.contains("WARNING")) + errorMessage = "Error while executing: " + command + LT + new String(msg); + } + + BufferedReader br = new BufferedReader(new InputStreamReader(process + .getInputStream())); + + StringBuffer sb = new StringBuffer(); + String line = null; + + while (true) { + line = br.readLine(); + if (line != null) { + sb.append(line); + sb.append(LT); + out.write(line); + out.write(LT); + } + if (line.endsWith(TERMINATOR)) + break; + } + if (interrupt) { + process.destroy(); + initializeShell(); + interrupt = false; + }else if (line != null && retCode != null) { + try { + retCode[0]=Integer.parseInt(line.substring(0,line.lastIndexOf(TERMINATOR))); + }catch (NumberFormatException e) { + throw new IOException("Can NOT get return code" + command + LT + line); + } + } + out.flush(); + if (errorMessage != null) { + throw new IOException(errorMessage); + } + return sb.toString(); + } + + synchronized + public void execute(String command) throws IOException { + interrupt = false; + String errorMessage = null; + + sendToProcessAndTerminate(command); + boolean cancel = false; + try { + InputStream is = process.getInputStream(); + InputStream es = process.getErrorStream(); + String info; + while (!cancel) { + info = null; + StringBuffer buffer = new StringBuffer(); + int c; + while (is.available() > 0) { + c = is.read(); + char ch = (char) c; + buffer.append(ch); + if (ch == '\n') { + info = buffer.toString(); + if (!info.trim().endsWith(TERMINATOR)) { + out.write(info); + out.write(LT); + buffer.delete(0, buffer.length()); + } else { + cancel = true; + break; + } + } + } + while (es.available() > 0) { + c = es.read(); + char ch = (char) c; + buffer.append(ch); + if (ch == '\n') { + info = buffer.toString(); + if (!info.contains("WARNING")) + errorMessage += info; + out.write(info); + out.write(LT); + buffer.delete(0, buffer.length()); + } + } + } + } catch (IOException e) { + try { + throw new InvocationTargetException(e); + } catch (InvocationTargetException e1) { + e1.printStackTrace(); + } + } + out.flush(); + if (errorMessage != null) { + throw new IOException(errorMessage); + } + if (interrupt) { + process.destroy(); + initializeShell(); + interrupt = false; + } + } + synchronized + public boolean ensureKnownHostKey(String user, String host) throws IOException { + + boolean loadKeysMatch = false; + boolean accepted = false; + Process proc = Runtime.getRuntime().exec("ssh -o LogLevel=DEBUG3 " + user + "@" + host); + Pattern patternLoad = Pattern.compile("^debug3: load_hostkeys: loaded (\\d+) keys"); + Pattern patternAuthSucceeded = Pattern.compile("^debug1: Authentication succeeded.*"); + + try { + InputStream es = proc.getErrorStream(); + String info; + while (!loadKeysMatch) { + info = null; + StringBuffer buffer = new StringBuffer(); + int c; + while (es.available() > 0) { + c = es.read(); + char ch = (char) c; + buffer.append(ch); + if (ch == '\r') { + info = buffer.toString().trim(); + Matcher m = patternLoad.matcher(info); + if(m.matches()) { + int keys = new Integer(m.group(1)); + if (keys == 0) { + proc.destroy(); + DialogRunnable runnable = new DialogRunnable("Host authenticity", "The authenticity of host '" + host + "(" + host + ")' can't be established.\nAre you sure you want to continue connecting ?", DialogRunnable.QUESTION); + Display.getDefault().syncExec(runnable); + accepted = runnable.result; + if (accepted){ + proc = Runtime.getRuntime().exec("ssh -o StrictHostKeyChecking=no " + user + "@" + host);//add host key to known_hosts + try { + Thread.sleep(2000); //wait for process to finish + } catch (InterruptedException e) { + e.printStackTrace(); + } + proc.destroy(); + } else { + Display.getDefault().syncExec( new DialogRunnable("Host authenticity", "Host key verification failed.", DialogRunnable.ERROR)); + } + } else { + String errorMsg = ""; + // wait to check if key is the same and authentication succeeds + while (es.available() > 0) { + c = es.read(); + ch = (char) c; + buffer.append(ch); + if (ch == '\r') { + info = buffer.toString().trim(); + Matcher mAuthS = patternAuthSucceeded.matcher(info); + if(mAuthS.matches()) { + accepted = true; + break; + } else { + if (!info.startsWith("debug")) + errorMsg += info + "\n"; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + buffer.delete(0, buffer.length()); + } + } + if (!accepted && !errorMsg.isEmpty()) { + Display.getDefault().syncExec( new DialogRunnable("Host authenticity", errorMsg, DialogRunnable.ERROR)); + } + } + loadKeysMatch = true; + break; + } + buffer.delete(0, buffer.length()); + } + } + } + es.close(); + } catch (IOException e) { + try { + throw new InvocationTargetException(e); + } catch (InvocationTargetException e1) { + e1.printStackTrace(); + } + } + return accepted; + } + + /** + * Send command string to shell process and add special terminator string so + * reader knows when output is complete. + * + * @param command + * @throws IOException + */ + private void sendToProcessAndTerminate(String command) throws IOException { + pos.write(command.getBytes()); + pos.write(LT.getBytes()); + pos.flush(); + pos.write("echo $?".getBytes()); + pos.write(TERMINATOR.getBytes()); + pos.write(LT.getBytes()); + pos.flush(); + } + + /** + * Interrupt any running processes. + */ + public void interrupt() { + interrupt = true; + } +} diff --git a/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/TerminalHandler.java b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/TerminalHandler.java new file mode 100644 index 0000000..d4798c2 --- /dev/null +++ b/plugins/org.yocto.remote.utils/src/org/yocto/remote/utils/TerminalHandler.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2013 Intel Corporation. + * 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: + * Intel - initial API and implementation + *******************************************************************************/ +package org.yocto.remote.utils; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jface.dialogs.ProgressMonitorDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.rse.core.model.IHost; +import org.eclipse.rse.internal.terminals.ui.TerminalServiceHelper; +import org.eclipse.rse.internal.terminals.ui.views.RSETerminalConnector; +import org.eclipse.rse.internal.terminals.ui.views.TerminalViewTab; +import org.eclipse.rse.internal.terminals.ui.views.TerminalViewer; +import org.eclipse.rse.internal.terminals.ui.views.TerminalsUI; +import org.eclipse.rse.services.terminals.ITerminalShell; +import org.eclipse.rse.subsystems.terminals.core.ITerminalServiceSubSystem; +import org.eclipse.rse.subsystems.terminals.core.elements.TerminalElement; +import org.eclipse.rse.ui.SystemBasePlugin; +import org.eclipse.swt.custom.CTabItem; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.tm.internal.terminal.control.ITerminalViewControl; +import org.eclipse.tm.internal.terminal.provisional.api.ITerminalConnector; + +abstract public class TerminalHandler extends AbstractHandler { + + + protected Shell shell; + + protected String changeTerm = "export TERM=vt100;"; + + abstract protected String getInitCmd(); + abstract protected String getConnnectionName(); + abstract protected String getDialogTitle(); + + protected String changeTerm() { + return changeTerm; + } + + protected ITerminalShell getTerminalShellFromTab(CTabItem item) { + ITerminalShell terminalShell = null; + ITerminalViewControl terminalViewControl = (ITerminalViewControl) item + .getData(TerminalViewTab.DATA_KEY_CONTROL); + ITerminalConnector terminalConnector = terminalViewControl + .getTerminalConnector(); + if (terminalConnector instanceof RSETerminalConnector) { + RSETerminalConnector rseTerminalConnector = (RSETerminalConnector) terminalConnector; + terminalShell = rseTerminalConnector.getTerminalHostShell(); + } + return terminalShell; + } + + protected boolean preProcess(final ITerminalServiceSubSystem terminalSubSystem) { + if (!terminalSubSystem.isConnected()) { + try { + ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell); + dialog.run(true, true, new IRunnableWithProgress(){ + @Override + public void run(IProgressMonitor monitor) { + monitor.beginTask("Connecting to remote target ...", 100); + try { + terminalSubSystem.connect(new NullProgressMonitor(), false); + monitor.done(); + } catch (Exception e) { + CommonHelper.showErrorDialog("Connection failure", null, e.getMessage()); + monitor.done(); + + } + } + }); + } catch (OperationCanceledException e) { + // user canceled, return silently + return false; + } catch (Exception e) { + SystemBasePlugin.logError(e.getLocalizedMessage(), e); + return false; + } + } else + return true; + return false; + } + + public void execute(IHost host) throws ExecutionException { + + final ITerminalServiceSubSystem terminalSubSystem = RSEHelper.getTerminalSubSystem(host); + + if (terminalSubSystem != null) { + TerminalsUI terminalsUI = TerminalsUI.getInstance(); + TerminalViewer viewer = terminalsUI.activateTerminalsView(); + if (preProcess(terminalSubSystem)) { + CTabItem tab = viewer.getTabFolder().createTabItem( + terminalSubSystem.getHost(), changeTerm() + getInitCmd()); + //since RSETerminalConnector not exit the shell during the diconnection, + //we have manually exit it here + try { + tab.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + Object source = e.getSource(); + if (source instanceof CTabItem) { + CTabItem currentItem = (CTabItem) source; + ITerminalShell shell=getTerminalShellFromTab(currentItem); + if(shell!=null) { + shell.exit(); + } + } + } + }); + }catch(Exception e) { + e.printStackTrace(); + } + TerminalElement element = TerminalServiceHelper + .createTerminalElement(tab, terminalSubSystem); + terminalSubSystem.addChild(element); + + } + } + } + +} -- cgit v1.2.3-54-g00ecf