From b5e8fe024dd87c0fd05bc0a168e938a457f2a55f Mon Sep 17 00:00:00 2001 From: Martin Oberhuber < martin.oberhuber@windriver.com> Date: Tue, 8 Aug 2006 16:57:55 +0000 Subject: [PATCH] Support shell output parsing in ssh connections --- .../build.properties | 1 + .../org.eclipse.rse.services.ssh/patterns.dat | 283 ++++++++++++++++++ .../eclipse/rse/services/ssh/Activator.java | 18 +- .../rse/services/ssh/SshServiceResources.java | 2 + .../ssh/SshServiceResources.properties | 2 + .../services/ssh/shell/CommandPattern.java | 60 ++++ .../rse/services/ssh/shell/OutputPattern.java | 98 ++++++ .../rse/services/ssh/shell/ParsedOutput.java | 38 +++ .../rse/services/ssh/shell/Patterns.java | 121 ++++++++ .../rse/services/ssh/shell/SshHostShell.java | 6 +- .../ssh/shell/SshShellOutputReader.java | 6 +- .../META-INF/MANIFEST.MF | 1 + .../shells/ssh/SshServiceCommandShell.java | 161 ++++++++++ .../ssh/SshShellSubSystemConfiguration.java | 8 + 14 files changed, 798 insertions(+), 7 deletions(-) create mode 100644 rse/plugins/org.eclipse.rse.services.ssh/patterns.dat create mode 100644 rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/CommandPattern.java create mode 100644 rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/OutputPattern.java create mode 100644 rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/ParsedOutput.java create mode 100644 rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/Patterns.java create mode 100644 rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshServiceCommandShell.java diff --git a/rse/plugins/org.eclipse.rse.services.ssh/build.properties b/rse/plugins/org.eclipse.rse.services.ssh/build.properties index af039aff2e1..1d891040fed 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/build.properties +++ b/rse/plugins/org.eclipse.rse.services.ssh/build.properties @@ -4,6 +4,7 @@ bin.includes = META-INF/,\ about.ini,\ about.mappings,\ about.properties,\ + patterns.dat,\ plugin.properties,\ readme.txt,\ eclipse32.png diff --git a/rse/plugins/org.eclipse.rse.services.ssh/patterns.dat b/rse/plugins/org.eclipse.rse.services.ssh/patterns.dat new file mode 100644 index 00000000000..d469576fc9b --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services.ssh/patterns.dat @@ -0,0 +1,283 @@ +################################################################################ +# Copyright (c) 2006 IBM 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 +# +# Initial Contributors: +# The following IBM employees contributed to the Remote System Explorer +# component that contains this file: David McKnight, Kushal Munir, +# Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, +# Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. +# +# Contributors: +# Martin Oberhuber (Wind River) - Adapted from RSE services.local/patterns.dat +################################################################################ +# +# parse command line output. +# +# CLICK THIS LINE TO EDIT THIS FILE (PATTERNS.DAT). (When viewing from the Output view) + +command: grep -n.* + grep file line pattern =([\w,.,/,(,),\\,\-,\+,\d]*):(\d*):.* + +command: grep -.*n.* + grep file line pattern =([\w,.,/,(,),\\,\-,\+,\d]*):(\d*):.* + +command: grep .* + grep file pattern =([\w,.,/,(,)\\,\-,\+,\d]*):.* + + +command: pwd + directory file pattern=([\w,.,/,(,),\\,\-,\+,\d,:]*) + +command: nmake.* + error pattern=NMAKE : .* + error file line pattern =([\w,.,\-,\+,(,),\\,:]*)\((\d*):(\d*)\) : error EDC.* + error file pattern =[\w,.,\-,\+,(,),\\,:]*\(([\w,.,/,\\]*)\) : error LNK.* + error file line pattern=([\w,.,/,\-,\+,(,),\\]*):(\d*):\d*\) : error .* + warning file line pattern=([\w,.,/,\-,\+,(,),\\]*)\((\d*):\d*\) : warning .* + informational file line pattern=([\w,.,/,\-,\+,(,),\\]*)\((\d*):\d*\) : informational .* + +command: make.* + error file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(S\).* + error file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\",, line (\d*)\.\d*: \d*-\d* \(E\).* + warning file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(W\).* + informational file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(I\).* + warning file line pattern =([\w,.,/,(,),\-,\+,\\]*):(\d*): warning: .* + warning file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):\d*: warning: .* + error file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):.* + informational file pattern =([\w,.,/,\\,\-,\+,(,)]*):.* + informational file line pattern =In file included from ([\w,.,/,\\,\-,\+,(,)]*):(\d*): + #iseries c compiler + error file line pattern=[\w,.,/,\\]* Line \d* \"([\w,.,/,\\]*)\", line (\d*)\.\d*: .* + error file line pattern=([\w,.,/,\\]*) Line (\d*)\s.* + #makefile errors + error file line pattern=\"([\w,\.,/,(,),\-,\+,\\]*)\", line (\d*): make:.* + #xlc Options warnings + warning pattern=\(W\) Option .* + #c89 errors + error file line pattern=ERROR CBC\d* ([\w,.,/,\\,\-,\+,(,)]*):(\d*) .* + warning file line pattern=WARNING CBC\d* ([\w,.,/,\\,\-,\+,(,)]*):(\d*) .* + informational pattern=CBC\d*\(I\) .* + +command: gmake.* + error file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(S\).* + warning file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(W\).* + informational file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(I\).* + warning file line pattern =([\w,.,/,(,),\-,\+,\\]*):(\d*): warning: .* + warning file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):\d*: warning: .* + informational file line pattern =In file included from ([\w,.,/,\\,\-,\+,(,)]*):(\d*): + # ixlc compiler + error file line pattern=[\w,.,/,\\]* Line \d* \"([\w,.,/,\\]*)\", line (\d*)\.\d*: .* + error file line pattern=([\w,.,/,\\]*) Line (\d*)\s.* + # icc compiler + error file line pattern =([\w,.,/,\\,\-,\+,(,)]*),\s(\d*)\.\d*:\s.* + error file line pattern =\"([\w,.,/,\\,\-,\+,(,)]*)\",\sline\s(\d*)\.\d*:\s.* + #generic + error file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):.* + +command: gcc\s.* + error file line pattern=\"([\w,.,/,\\,\-,\+,(,)]*)\", line (\d*)\.\d*: \d*-\d* \(S\).* + warning file line pattern=\"([\w,.,/,\\,\-,\+,(,)]*)\", line (\d*)\.\d*: \d*-\d* \(W\).* + informational file line pattern=\"([\w,.,/,\\,\-,\+,(,)]*)\", line (\d*)\.\d*: \d*-\d* \(I\).* + warning file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*): warning: .* + warning file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):\d*: warning: .* + error file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):.* + informational file pattern =([\w,.,/,\\,\-,\+,(,)]*):.* + informational file line pattern =In file included from ([\w,.,/,\\,\-,\+,(,)]*):(\d*): + +command: cc\s.* + error file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):.* + +command: CC\s.* + error file line pattern =([\w,.,/,\\,\-,\+,(,)]*):(\d*):.* + +command: c89.* + error file line pattern=ERROR CBC\d* ([\w,.,/,\\,\-,\+,(,)]*):(\d*) .* + warning file line pattern=WARNING CBC\d* ([\w,.,/,\\,\-,\+,(,)]*):(\d*) .* + informational pattern=CBC\d*\(I\) .* + +command: icc.* + error file line pattern =([\w,.,/,\\,\-,\+,(,)]*),\s(\d*)\.\d*:\s.* + error file line pattern =\"([\w,.,/,\\,\-,\+,(,)]*)\",\sline\s(\d*)\.\d*:\s.* + +command: javac.* + warning file line pattern=([\w,.,/,\\,\-,\+,(,),:]*):(\d*): warning.* + error file line pattern=([\w,.,/,\\,\-,\+,(,),:]*):(\d*):.* + +command: .*bat\s.* + error file line pattern=\[javac\]\s([\w,.,/,\\,:]*):(\d*):.* + error file line pattern=file:([\w,.,/,\\,:]*):(\d*):.* + +command: dir.* + directory file pattern=\d{2}/\d{2}/\d*\s*\d{2}:\d{2}\s[a,p,AM,PM]+\s*\s*([\w,.,/,\\,\s,\-,\+,\d]*).* + directory file pattern=.*\s*([\w,.,/,\\,\s,\-,\+,\d]*).* + file file pattern=\d{2}/\d{2}/\d*\s*\d{2}:\d{2}\s[a,p,AM,PM]+\s*[\d,,]*\s*([\w,.,/,\\,\s,\-,\+,\d]*).* + directory file pattern=[\d,/,\\,.,-]+.\s*\d{2}[:,.]\d{2}.\s*\s*([\w,.,/,\\,\s,\-,\+,\d]*) + file file pattern=[\d,/,\\,.,-]+.\s*\d{2}[:,.]\d{2}.\s*[\d,,,.]*\s*([\w,.,/,\\,\s,\-,\+,\d]*) + +command: ls -l.* + file file pattern=-[-,r,w,x]+\s.*\s[\d{4},\d{2}:\d{2}]+,\s([\w,.,/,(,),\\,\-,\+\s,\d]*) + file file pattern=l[-,r,w,x]+\s.*\s[\d{4},\d{2}:\d{2}]+,\s([\w,.,/,(,),\\,\-,\+\s,\d]*)\s->\s[\w,.,/,(,),\\,\-,\+\s,\d]* + file file pattern=-.*\d{2}:\d{2} ([\w,.,/,(,),\\,\-,\+,\s,\d]*) + file file pattern=-.* \d{4} ([\w,.,/,(,),\\,\-,\+,\s,\d]*) + file file pattern=l.*\d{2}:\d{2} ([\w,.,/,(,),\\,\-,\+,\s,\d]*)\s->\s[\w,.,/,(,),\\,\-,\+\s,\d]* + file file pattern=l.* \d{4} ([\w,.,/,(,),\\,\-,\+,\s,\d]*)\s->\s[\w,.,/,(,),\\,\-,\+\s,\d]* + directory file pattern=d.*\d{2}:\d{2} ([\w,.,/,(,),\\,\-,\+\s,\d]*) + directory file pattern=d.*\d{4} ([\w,.,/,(,),\\,\-,\+,\s,\d]*) + +command: ls -a.* + file file pattern=-.*\d{2}:\d{2} ([\w,.,/,(,),\\,\-,\+\s,\d]*) + file file pattern=-.* \d{4} ([\w,.,/,(,),\\,\-,\+\s,\d]*) + directory file pattern=d.*\d{2}:\d{2} ([\w,.,/,(,),\\,\-,\+\s,\d]*) + directory file pattern=d.*\d{4} ([\w,.,/,(,),\\,\-,\+\s,\d]*) + +command: ls + file file pattern=([\w,\d,.,(,),\-,\+\s]+) + +command: ls\s.* + file file pattern=([\w,\d,.,(,),\-,\+\s]+) + +command: .* + prompt file pattern=([\w,.,/,(,),\\,\-,\+,\d,:,\s]*)>.* + +command: find .* + file file pattern=([\w,.,/,\\,(,),\-]+) + +command: env + pathenvvar file pattern=PATH\=(.*) + libpathenvvar file pattern=LIBPATH\=(.*) + libpathenvvar file pattern=LIB\=(.*) + envvar pattern=[\w\d]+\=.* + +command: ps + process pattern=[\s]*[\d]+\s.* + +command: ps .* + process pattern=[\s]*[\d]+\s.* + +command: xl[C,c].* + error file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\",, line (\d*)\.\d*: \d*-\d* \(S\).* + error file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(E\).* + warning file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(W\).* + informational file line pattern=\"([\w,.,/,(,),\-,\+,\\]*)\", line (\d*)\.\d*: \d*-\d* \(I\).* + +command: ixlc.* + error file line pattern=([\w,.,/,\\]*) Line (\d*)\s.* + error file line pattern=[\w,.,/,\\]* Line \d* \"([\w,.,/,\\]*)\", line (\d*)\.\d*: .* + +command: cat.*patterns.* + warning file pattern=# .*(PATTERNS.DAT).* + Output pattern=command: .* + #statement pattern=#.* + statement pattern=.* pattern.*=.* + +# special for autoconf +command: ./bootstrap.sc.* + error file line pattern=aclocal: ([\w,.,/,\\]*): (\d*):.* + warning file line pattern=([\w,.,/,\\]*):(\d*):.* + +command: ./configure.* + error file line pattern=./(configure): line (\d*): .* + +# How Patterns Work +# ----------------- +# +# Patterns are used by the Output View to allow lines that contain file names and +# perhaps line numbers to be clicked to cause the file location to be opened in an Editor. +# A line in this file can be 1 of 3 things: +# 1. An empty line or a comment line (denoted by a # in column 1) +# 2. The start of a command description (denoted by a 'command:' at the beginning of the line) +# 3. A pattern description. +# A formal description of the syntax of this file is included at the bottom of this file. +# +# +# Command Descriptions +# -------------------- +# +# A Command Description consists of 2 things: A regular expression that describes the +# command invocation, and a list of Pattern Descriptions. The regular expression +# is used to determine what set of Pattern Descriptons to use when parsing command output. +# For example, if you type the command 'gmake clean', the Command Descriptions are checked and +# .*make.* would match that command. Therefore the Pattern Descriptions for .*make.* would +# be used to parse the output of the 'gmake clean'. +# Note: The first Command Description that is found to match the command is used, so make +# sure you put the most specific patterns first. For example, if the nmake.* Command +# Description appeared after .*make.*, then 'nmake install' would be matched by the .*make.* +# Command Descripton, which is probably not what was intended. +# +# +# Pattern Descriptions +# -------------------- +# +# A Pattern Description has the following form: +# "pattern=" +# +# where: +# : The type of object that will be created in the Output View if +# a line of output matches this pattern. +# +# : This is some combination of the words "file" and "line" or nothing +# at all. This is used to define how the backreferences in the +# regular expression are to be interpreted. So "file line" means +# that the first back-reference is a file, and the second is a +# line number. This may seem unnecessary, but we added this capability +# in the event that future releases support other types of information +# that may be useful, such as column info (once we support it). +# +# : A regular expression that describes a line of output. Backreferences +# are used to store (instead of just match) the filename and line +# number. To store a filename use the backreference ([\w,.,/,\\]*), and +# to store a line number, use (\d*) +# +# +# Note: The patterns are checked against command output and only exact matches are dealt with +# So as an example, if you forget the .* (match everything) at the end of a pattern, +# the match will fail if there are trailing characters in the output not accounted for by +# the pattern +# +# +# Pattern File Syntax +# ------------------- +# +# The pattern file syntax should be easy to grasp by looking at the ones +# above, but for those of you who like formal grammars, here it is: +# +# patterns-file: +# commands +# +# commands: +# command +# commands new-line command +# +# command: +# "command:" reg-ex new-line patterns +# +# patterns: +# pattern +# patterns new-line pattern +# +# pattern: +# command-name match-list "pattern=" reg-ex +# +# command-name: +# String that denotes what type of object will be created if some output matches this pattern +# +# match-list: +# file-or-line +# match-list space file-or-line +# +# file-or-line: +# "file" | "line" +# +# reg-ex: +# A regular expression +# +# new-line: +# The new-line character \n +# +# space: +# The space character + + diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/Activator.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/Activator.java index 4b470373e00..77c004b0123 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/Activator.java +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/Activator.java @@ -15,8 +15,12 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; import org.osgi.framework.BundleContext; @@ -61,7 +65,19 @@ public class Activator extends Plugin { public static Activator getDefault() { return plugin; } - + + /** + * Logs an throwable to the log for this plugin. + * @param t the Throwable to be logged. + */ + public void logException(Throwable t) { + ILog log = getLog(); + String id = getBundle().getSymbolicName(); + String message = NLS.bind(SshServiceResources.SshPlugin_Unexpected_Exception, t.getClass().getName(), t.getLocalizedMessage()); + IStatus status = new Status(IStatus.ERROR, id, 0, message, t); + log.log(status); + } + //---------------------------------------------------- private static Boolean fTracingOn = null; diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.java index 13e3152d956..3947737b221 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.java +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.java @@ -16,6 +16,8 @@ import org.eclipse.osgi.util.NLS; public class SshServiceResources extends NLS { private static final String BUNDLE_NAME = "org.eclipse.rse.services.ssh.SshServiceResources"; //$NON-NLS-1$ + public static String SshPlugin_Unexpected_Exception; + public static String SftpFileService_Description; public static String SftpFileService_Error_JschSessionLost; diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.properties b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.properties index d9efcf5d040..88d9556a7f3 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.properties +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/SshServiceResources.properties @@ -9,6 +9,8 @@ # Martin Oberhuber (Wind River) - initial API and implementation ################################################################################ +SshPlugin_Unexpected_Exception=Unexpected {0}: {1} + SftpFileService_Name=Ssh / Sftp File Service SftpFileService_Description=Access a remote file system via Ssh / Sftp protocol SftpFileService_Error_JschSessionLost=jsch session lost diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/CommandPattern.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/CommandPattern.java new file mode 100644 index 00000000000..73dafd03404 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/CommandPattern.java @@ -0,0 +1,60 @@ +/******************************************************************************** + * Copyright (c) 2001, 2006 IBM Corporation and International Business Machines 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 + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * Martin Oberhuber (WindRiver) - adapted from services.local + ********************************************************************************/ + +package org.eclipse.rse.services.ssh.shell; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +public class CommandPattern +{ + + + private Pattern _pattern; + private ArrayList _outputPatterns; + + public CommandPattern(Pattern theCommandPattern) + { + _pattern = theCommandPattern; + _outputPatterns = new ArrayList(); + } + + public void addOutputPattern(OutputPattern op) + { + _outputPatterns.add(op); + } + + public boolean matchCommand(String theLine) + { + return _pattern.matcher(theLine).matches(); + } + + public ParsedOutput matchLine(String theLine) + { + int patterns = _outputPatterns.size(); + ParsedOutput matchedOutput; + OutputPattern curPattern; + for (int i = 0; i < patterns; i++) + { + curPattern = (OutputPattern) _outputPatterns.get(i); + matchedOutput = curPattern.matchLine(theLine); + if (matchedOutput != null) + return matchedOutput; + } + return null; + } + +} \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/OutputPattern.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/OutputPattern.java new file mode 100644 index 00000000000..bb86d09fa76 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/OutputPattern.java @@ -0,0 +1,98 @@ +/******************************************************************************** + * Copyright (c) 2001, 2006 IBM Corporation and International Business Machines 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 + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * Martin Oberhuber (WindRiver) - adapted from services.local + ********************************************************************************/ + +package org.eclipse.rse.services.ssh.shell; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OutputPattern +{ + + + private Pattern _pattern; + private String _objType; + private ArrayList _matchOrder; + + public OutputPattern(String objType, String matchOrder, Pattern thePattern) + { + _objType = objType; + _pattern = thePattern; + + _matchOrder = new ArrayList(); + //Here we add a dummy first element to the ArrayList, to mimick how the PatternMatcher stores it's + //matches (starting with group 1). + _matchOrder.add(null); + + int index = 0; + int nextSpace = 0; + //Walk the matchOrder string parsing out words and adding them to _matchOrder...Could use StringTokenizer + //but this seem much simpler. + while ((nextSpace = matchOrder.indexOf(" ", index)) > 0) //$NON-NLS-1$ + { + _matchOrder.add(matchOrder.substring(index, nextSpace).toLowerCase()); + index = nextSpace; + while ((index < matchOrder.length()) && (matchOrder.charAt(index) == ' ')) + index++; + } + _matchOrder.add(matchOrder.substring(index, matchOrder.length()).toLowerCase()); + + } + + public ParsedOutput matchLine(String theLine) + { + Matcher matcher = null; + try + { + matcher = _pattern.matcher(theLine); + if (!matcher.matches()) + return null; + } + catch (StringIndexOutOfBoundsException e) + { + //Getting an exception here, when theLine is an empty line for some patterns..should probably investigate, + //but for now we'll just handle it... + return null; + } + + String fileString = ""; //$NON-NLS-1$ + String lineString = ""; //$NON-NLS-1$ + //Groups start at 1 (group 0 is the entire match). + for (int i = 1; i < _matchOrder.size(); i++) + { + String mStr = (String)_matchOrder.get(i); + if (mStr.equals("file")) //$NON-NLS-1$ + fileString = matcher.group(i); + else if (mStr.equals("line")) //$NON-NLS-1$ + lineString = matcher.group(i); + } + int line = 1; + if (lineString.length() > 0) + { + try + { + line = Integer.parseInt(lineString); + } + catch (NumberFormatException e) + { + } + } + + return new ParsedOutput(_objType, theLine, fileString, line, 1); + } + +} \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/ParsedOutput.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/ParsedOutput.java new file mode 100644 index 00000000000..5ec2ccb706a --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/ParsedOutput.java @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2002, 2006 IBM 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 + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * Martin Oberhuber (WindRiver) - adapted from services.local + ********************************************************************************/ + +package org.eclipse.rse.services.ssh.shell; + +//This is just a convenience object for storing information parsed out of a line of output. +public class ParsedOutput +{ + + + public String type; + public String text; + public String file; + public int line; + public int col; + + public ParsedOutput (String theType, String theText, String theFile, int theLine, int theColumn) + { + type = theType; + text = theText; + file = theFile; + line = theLine; + col = theColumn; + } +} \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/Patterns.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/Patterns.java new file mode 100644 index 00000000000..d8ff2dfa19e --- /dev/null +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/Patterns.java @@ -0,0 +1,121 @@ +/******************************************************************************** + * Copyright (c) 2002, 2006 IBM 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 + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * Martin Oberhuber (WindRiver) - adapted from services.local + ********************************************************************************/ +package org.eclipse.rse.services.ssh.shell; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.regex.Pattern; + +import org.osgi.framework.Bundle; + +import org.eclipse.rse.services.ssh.Activator; + +public class Patterns { + private ArrayList _theCommands; + private String _currentCommand; + + public Patterns() { + _theCommands = new ArrayList(); + parsePatterns(); + } + + private void parsePatterns() { + Bundle bundle = Activator.getDefault().getBundle(); + URL patterns = bundle.getEntry("/patterns.dat"); //$NON-NLS-1$ + if (patterns != null) { + try { + InputStream in = patterns.openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + _theCommands.clear(); + String curLine; + CommandPattern curCommand = null; + while ((curLine = reader.readLine()) != null) { + curLine = curLine.trim(); + // Skip the current line if it is empty or starts with a # + if ((curLine.length() == 0) || (curLine.charAt(0) == '#')) { + continue; + } + // Check if this line is the start of a new command section + if (curLine.startsWith("command")) { //$NON-NLS-1$ + int colon = curLine.indexOf(":"); //$NON-NLS-1$ + // Check that there is something after the colon + if (colon == (curLine.length() - 1)) { + continue; + } + Pattern thePattern = Pattern.compile(curLine.substring(colon + 1, curLine.length()).trim()); + curCommand = new CommandPattern(thePattern); + _theCommands.add(curCommand); + } + // If we get here, the line must be an output pattern + else { + int firstSpace = curLine.indexOf(" "); //$NON-NLS-1$ + int patternWord = curLine.indexOf("pattern"); //$NON-NLS-1$ + int firstEquals = curLine.indexOf("="); //$NON-NLS-1$ + if ((firstEquals == -1) || (firstEquals == (curLine.length() - 1))) { + continue; + } + String objType = curLine.substring(0, firstSpace); + String matchOrder = curLine.substring(firstSpace + 1, patternWord).trim(); + String patternString = curLine.substring(firstEquals + 1, curLine.length()); + Pattern thePattern = Pattern.compile(patternString.trim()); + if (curCommand != null) { + curCommand.addOutputPattern(new OutputPattern(objType, matchOrder, thePattern)); + } + } + } + in.close(); + } catch (IOException e) { + Activator.getDefault().logException(e); + } + } + } + + public void refresh(String theCommand) { + _currentCommand = theCommand; + parsePatterns(); + } + + public void update(String theCommand) { + _currentCommand = theCommand; + } + + public ParsedOutput matchLine(String theLine) { + CommandPattern curCommand; + ParsedOutput matchedOutput = null; + int commands = _theCommands.size(); + if (_currentCommand != null) + { + + for (int i = 0; i < commands; i++) + { + curCommand = (CommandPattern) _theCommands.get(i); + if (curCommand.matchCommand(_currentCommand)) + { + matchedOutput = curCommand.matchLine(theLine); + } + if (matchedOutput != null) + { + return matchedOutput; + } + } + } + return null; + } +} \ No newline at end of file diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshHostShell.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshHostShell.java index 8c6c9f14768..714b908057d 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshHostShell.java +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshHostShell.java @@ -20,6 +20,7 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; +import java.util.regex.Pattern; import org.eclipse.core.runtime.IProgressMonitor; @@ -66,7 +67,10 @@ public class SshHostShell extends AbstractHostShell implements IHostShell { PrintWriter outputWriter = new PrintWriter(outputStream); fShellWriter = new SshShellWriterThread(outputWriter); fChannel.connect(); - if (initialWorkingDirectory!=null && initialWorkingDirectory.length()>0 && !initialWorkingDirectory.equals(".")) { //$NON-NLS-1$ + if (initialWorkingDirectory!=null && initialWorkingDirectory.length()>0 + && !initialWorkingDirectory.equals(".") //$NON-NLS-1$ + && !initialWorkingDirectory.equals("Command Shell") //$NON-NLS-1$ //FIXME workaround for bug 153047 + ) { writeToShell("cd "+initialWorkingDirectory); //$NON-NLS-1$ } } catch(Exception e) { diff --git a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshShellOutputReader.java b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshShellOutputReader.java index 0632a25cbbf..9ae4b5154c5 100644 --- a/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshShellOutputReader.java +++ b/rse/plugins/org.eclipse.rse.services.ssh/src/org/eclipse/rse/services/ssh/shell/SshShellOutputReader.java @@ -20,9 +20,6 @@ package org.eclipse.rse.services.ssh.shell; import java.io.BufferedReader; import java.io.IOException; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; - import org.eclipse.rse.services.shells.AbstractHostShellOutputReader; import org.eclipse.rse.services.shells.IHostShell; import org.eclipse.rse.services.shells.IHostShellOutputReader; @@ -142,8 +139,7 @@ public class SshShellOutputReader extends AbstractHostShellOutputReader //FIXME it's dangerous to return null here since this will end //our reader thread completely... the exception could just be //temporary, and we should keep running! - Activator.getDefault().getLog().log(new Status(IStatus.WARNING, Activator.getDefault().getBundle().getSymbolicName(), 0, "IOException in SshShellOutputReader", e)); - //e.printStackTrace(); + Activator.getDefault().logException(e); return null; } } diff --git a/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/META-INF/MANIFEST.MF b/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/META-INF/MANIFEST.MF index 40bfa66192e..90ac008d6a9 100644 --- a/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/META-INF/MANIFEST.MF +++ b/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/META-INF/MANIFEST.MF @@ -12,6 +12,7 @@ Require-Bundle: org.eclipse.ui, org.eclipse.rse.connectorservice.ssh, org.eclipse.rse.services.ssh, org.eclipse.rse.subsystems.shells.core, + org.eclipse.rse.subsystems.files.core, org.eclipse.rse.shells.ui, org.eclipse.rse.ui Eclipse-LazyStart: true diff --git a/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshServiceCommandShell.java b/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshServiceCommandShell.java new file mode 100644 index 00000000000..44b798b2a17 --- /dev/null +++ b/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshServiceCommandShell.java @@ -0,0 +1,161 @@ +/******************************************************************************** + * Copyright (c) 2006 IBM 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 + * + * Initial Contributors: + * The following IBM employees contributed to the Remote System Explorer + * component that contains this file: David McKnight, Kushal Munir, + * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, + * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley. + * + * Contributors: + * Martin Oberhuber (Wind River) - Adapted from LocalServiceCommandShell + ********************************************************************************/ + +package org.eclipse.rse.subsystems.shells.ssh; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +import org.eclipse.rse.core.subsystems.ISubSystem; +import org.eclipse.rse.internal.subsystems.shells.servicesubsystem.OutputRefreshJob; +import org.eclipse.rse.internal.subsystems.shells.subsystems.RemoteError; +import org.eclipse.rse.internal.subsystems.shells.subsystems.RemoteOutput; +import org.eclipse.rse.services.shells.IHostShell; +import org.eclipse.rse.services.shells.IHostShellChangeEvent; +import org.eclipse.rse.services.ssh.shell.ParsedOutput; +import org.eclipse.rse.services.ssh.shell.Patterns; +import org.eclipse.rse.subsystems.files.core.subsystems.IRemoteFileSubSystem; +import org.eclipse.rse.subsystems.shells.core.model.ISystemOutputRemoteTypes; +import org.eclipse.rse.subsystems.shells.core.subsystems.IRemoteCmdSubSystem; +import org.eclipse.rse.subsystems.shells.core.subsystems.IRemoteOutput; +import org.eclipse.rse.subsystems.shells.core.subsystems.servicesubsystem.ServiceCommandShell; + +public class SshServiceCommandShell extends ServiceCommandShell implements ISystemOutputRemoteTypes +{ + private Patterns _patterns; + private String _curCommand; + private String _workingDir; + private IRemoteFileSubSystem _fs; + + public SshServiceCommandShell(IRemoteCmdSubSystem cmdSS, IHostShell hostShell) + { + super(cmdSS, hostShell); + _patterns = new Patterns(); + _patterns.update("cmd"); //$NON-NLS-1$ + ISubSystem[] sses = cmdSS.getHost().getSubSystems(); + for (int i = 0; i < sses.length; i++) + { + if (sses[i] instanceof IRemoteFileSubSystem) + { + _fs = (IRemoteFileSubSystem)sses[i]; + } + } + } + + public Object getContext() + { + String workingDir = _workingDir; + if (workingDir != null && workingDir.length() > 0) + { + try + { + return _fs.getRemoteFileObject(workingDir); + } + catch (Exception e) + { + } + } + return null; + + } + + public void shellOutputChanged(IHostShellChangeEvent event) + { + Object[] lines = event.getLines(); + boolean gotCommand = false; + ArrayList outputs = new ArrayList(lines.length); + for (int i = 0; i < lines.length; i++) + { + String line = (String)lines[i]; + if (line.endsWith(getPromptCommand())) { + continue; //ignore our synthetic prompt command + } + + ParsedOutput parsedMsg = null; + if (!gotCommand && line.equals(_curCommand)) { + gotCommand = true; + } else { + try { + parsedMsg = _patterns.matchLine(line); + } + catch (Throwable e) { + e.printStackTrace(); + } + } + RemoteOutput output = null; + String type = "stdout"; //$NON-NLS-1$ + if (parsedMsg != null) { + type = parsedMsg.type; + } + if (event.isError()) { + output = new RemoteError(this, type); + } + else { + output = new RemoteOutput(this, type); + } + + output.setText(line); + if (parsedMsg != null) + { + String file = parsedMsg.file; + if (type.equals("prompt")) //$NON-NLS-1$ + { + _workingDir = file; + output.setAbsolutePath(_workingDir); + } + else if(_workingDir!=null) + { + IPath p = new Path(_workingDir).append(file); + output.setAbsolutePath(p.toString()); + } + else + { + output.setAbsolutePath(file); + } + } + + addOutput(output); + outputs.add(output); + } + if (_lastRefreshJob == null || _lastRefreshJob.isComplete()) + { + IRemoteOutput[] remoteOutputs = (IRemoteOutput[])outputs.toArray(new IRemoteOutput[outputs.size()]); + _lastRefreshJob = new OutputRefreshJob(this, remoteOutputs, false); + _lastRefreshJob.schedule(); + } + } + + private static final Pattern cdCommands = Pattern.compile("\\A\\s*(cd|chdir|ls)\\b"); //$NON-NLS-1$ + + protected String getPromptCommand() { + return "echo $PWD'>'"; //$NON-NLS-1$ + } + + public void writeToShell(String cmd) + { + _curCommand = cmd; + _patterns.update(cmd); + if (cdCommands.matcher(cmd).find()) { + cmd += "\r\n" + getPromptCommand(); //$NON-NLS-1$ + } + super.writeToShell(cmd); + + } + +} diff --git a/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshShellSubSystemConfiguration.java b/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshShellSubSystemConfiguration.java index 26e84d01336..ac009632787 100644 --- a/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshShellSubSystemConfiguration.java +++ b/rse/plugins/org.eclipse.rse.subsystems.shells.ssh/src/org/eclipse/rse/subsystems/shells/ssh/SshShellSubSystemConfiguration.java @@ -21,9 +21,12 @@ import org.eclipse.rse.connectorservice.ssh.SshConnectorServiceManager; import org.eclipse.rse.core.subsystems.IConnectorService; import org.eclipse.rse.core.subsystems.ISubSystem; import org.eclipse.rse.model.IHost; +import org.eclipse.rse.services.shells.IHostShell; import org.eclipse.rse.services.shells.IShellService; import org.eclipse.rse.services.ssh.ISshService; import org.eclipse.rse.services.ssh.shell.SshShellService; +import org.eclipse.rse.subsystems.shells.core.subsystems.IRemoteCmdSubSystem; +import org.eclipse.rse.subsystems.shells.core.subsystems.servicesubsystem.IServiceCommandShell; import org.eclipse.rse.subsystems.shells.core.subsystems.servicesubsystem.ShellServiceSubSystem; import org.eclipse.rse.subsystems.shells.core.subsystems.servicesubsystem.ShellServiceSubSystemConfiguration; @@ -72,4 +75,9 @@ public class SshShellSubSystemConfiguration extends return ISshService.class; } + public IServiceCommandShell createRemoteCommandShell(IRemoteCmdSubSystem cmdSS, IHostShell hostShell) + { + return new SshServiceCommandShell(cmdSS, hostShell); + } + }