| /****************************************************************************** |
| * 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 |
| * |
| * Contributors: |
| * IBM Corporation - Initial Implementation |
| * |
| *****************************************************************************/ |
| package org.eclipse.ptp.remotetools.internal.ssh; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.ptp.remotetools.core.IRemoteExecutionTools; |
| import org.eclipse.ptp.remotetools.core.IRemoteFileTools; |
| import org.eclipse.ptp.remotetools.core.IRemoteStatusTools; |
| import org.eclipse.ptp.remotetools.core.messages.Messages; |
| import org.eclipse.ptp.remotetools.exception.CancelException; |
| import org.eclipse.ptp.remotetools.exception.RemoteConnectionException; |
| import org.eclipse.ptp.remotetools.exception.RemoteExecutionException; |
| import org.eclipse.ptp.remotetools.exception.RemoteOperationException; |
| |
| |
| /** |
| * Concrete class responsible for returning status data from the remote host |
| * |
| * NOTE: This implementation assumes that the remote host is Linux based |
| * |
| * @author Richard Maciel |
| * |
| */ |
| public class StatusTools implements IRemoteStatusTools { |
| private ExecutionManager manager; |
| private UserInformation userInfoCache; |
| |
| /** |
| * This class is responsible for caching information that is "stable" enough to be stored and retrieved |
| * instead of generated every time. |
| * |
| * @author Richard Maciel |
| * |
| */ |
| class UserInformation { |
| private Integer userID; |
| private Set<Integer> groupIDSet; |
| private String username; |
| |
| public UserInformation(Integer userID, Set<Integer> groupIDSet, String username) { |
| this.userID = userID; |
| this.groupIDSet = groupIDSet; |
| this.username = username; |
| } |
| |
| /** |
| * @return the userID |
| */ |
| public Integer getUserID() { |
| return userID; |
| } |
| |
| /** |
| * @return the groupIDSet |
| */ |
| public Set<Integer> getGroupIDSet() { |
| return groupIDSet; |
| } |
| |
| /** |
| * @return the username |
| */ |
| public String getUsername() { |
| return username; |
| } |
| } |
| |
| protected StatusTools(ExecutionManager manager) { |
| this.manager = manager; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ptp.remotetools.core.IRemoteStatusTools#getRemotePortsInUse(int) |
| */ |
| public Set<Integer> getRemotePortsInUse(int protocol) throws RemoteConnectionException, RemoteOperationException, CancelException { |
| // Uses the following shell script to get a list of used ports in the |
| // remote host: |
| // cat /proc/net/<transport protocol> |
| // Then, the data coming from the command is filtered using java commands equivalent to the |
| // following shell command: |
| // cat /proc/net/tcp | tail -n +2 | sed -r 's/^\s+//g' | cut -d ' ' -f 2 | cut -d ':' -f 2 |
| |
| |
| HashSet<Integer> portSet = new HashSet<Integer>(); |
| String protoStr = null, protoStr6 = null; |
| if(protocol == IRemoteStatusTools.PROTO_UDP) { |
| protoStr = "cat /proc/net/udp"; //$NON-NLS-1$ |
| protoStr6 = "cat /proc/net/udp6"; //$NON-NLS-1$ |
| } else if (protocol == IRemoteStatusTools.PROTO_TCP){ |
| protoStr = "cat /proc/net/tcp"; //$NON-NLS-1$ |
| protoStr6 = "cat /proc/net/tcp6"; //$NON-NLS-1$ |
| } |
| |
| try { |
| IRemoteExecutionTools remExecTools = manager.getExecutionTools(); |
| |
| // Filter information from files. |
| /* |
| * "protoOutput" and "proto6Output" are the |
| * outputs of the above commands, WITHOUT the header |
| * that is being wiped out by the "replaceFirst" |
| * method. Thus, the resulting string |
| * "rawProcOutput" contains only valid connection |
| * entries for both ipv4 and ipv6 and must be |
| * totally processed on the following loop. |
| * |
| */ |
| String protoOutput = remExecTools.executeWithOutput(protoStr). |
| replaceFirst("^\\p{Space}*sl.*inode\\p{Space}*\\n", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| String proto6Output = remExecTools.executeWithOutput(protoStr6). |
| replaceFirst("^\\p{Space}*sl.*inode\\p{Space}*\\n", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| String rawProcOutput = protoOutput.concat(proto6Output); |
| |
| String [] rawProcLines = rawProcOutput.split("\n"); //$NON-NLS-1$ |
| |
| for(int i=0; i < rawProcLines.length; i++) { |
| // Trim the beginning of each line, and split the string using |
| // whitespace as separator. |
| String [] procFields = rawProcLines[i].trim().split(" "); //$NON-NLS-1$ |
| |
| // Get the second field and split it using ':' as separator. |
| // Get the second field. |
| String [] addrFields = procFields[1].split(":"); //$NON-NLS-1$ |
| String allocedPort = addrFields[1]; |
| |
| // Convert the value to an integer (it's on base 16) |
| // and put it into the set. |
| portSet.add(Integer.valueOf(allocedPort, 16)); |
| } |
| } catch (RemoteExecutionException e) { |
| throw new RemoteOperationException(e); |
| } |
| |
| return portSet; |
| } |
| |
| private static int PASSWD_USERNAME_FIELD = 0; |
| private static int PASSWD_USERID_FIELD = 2; |
| private static int PASSWD_GROUPID_FIELD = 3; |
| private static int PASSWD_HOMEDIR_FIELD = 5; |
| |
| private static int GROUP_GROUPID_FIELD = 2; |
| private static int GROUP_USERLIST_FIELD = 3; |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ptp.remotetools.core.IRemoteStatusTools#getGroupIDSet() |
| */ |
| public Set<Integer> getGroupIDSet() throws RemoteConnectionException, RemoteOperationException, CancelException { |
| // If there's already information on cache, use it |
| if(userInfoCache == null) { |
| userInfoCache = fetchRemoteUserInfo(); |
| } |
| return userInfoCache.getGroupIDSet(); |
| } |
| |
| /** |
| * An implementation of the {@link getGroupIDSet} method that only uses the cat command on the remote machine |
| * to extract desired data. Specifically, it retrieves information from the /etc/passwd looking and /etc/group files |
| * and extract the information about the groups of the user whose username comes from the {@link getUsername} method |
| * |
| * @return |
| * @throws RemoteConnectionException |
| * @throws RemoteOperationException |
| * @throws CancelException |
| */ |
| private Set<Integer> getGroupIDSet_CatBased() throws RemoteConnectionException, RemoteOperationException, CancelException { |
| String [] passwdFields = getPasswdFields(); |
| String username = passwdFields[PASSWD_USERNAME_FIELD].trim(); |
| String strPasswdGroupID = passwdFields[PASSWD_GROUPID_FIELD].trim(); |
| Set<Integer> groupIDSet = new HashSet<Integer>(); |
| |
| // Put the the group from the passd file into the list (if not blank) |
| if(!strPasswdGroupID.trim().equals("")) { //$NON-NLS-1$ |
| groupIDSet.add(new Integer(Integer.parseInt(strPasswdGroupID))); |
| } |
| |
| /* |
| * Get the group file and search for the groups that the user belongs |
| * to. |
| */ |
| // Cat the /etc/group file and split it in lines |
| IRemoteExecutionTools remExecTools = manager.getExecutionTools(); |
| try { |
| String rawGroup = remExecTools.executeWithOutput("cat /etc/group"); //$NON-NLS-1$ |
| String [] rawGroupLines = rawGroup.split("\\n"); //$NON-NLS-1$ |
| |
| for(int i=0; i < rawGroupLines.length; i++) { |
| // Last field can be non-existant, so use a negative parameter in split to |
| // make sure it becomes a field |
| String [] groupFields = rawGroupLines[i].split(":", -1); //$NON-NLS-1$ |
| // Search for the username in the list of users of the group. |
| // IF the username belongs to the group, insert it into the list. |
| if(isUsernameInList(username, groupFields[GROUP_USERLIST_FIELD].split(","))) { //$NON-NLS-1$ |
| groupIDSet.add(new Integer(Integer.parseInt(groupFields[GROUP_GROUPID_FIELD].trim()))); |
| } |
| } |
| } catch (RemoteExecutionException e) { |
| throw new RemoteOperationException(e); |
| } |
| // Returns the set |
| return groupIDSet; |
| } |
| |
| /** |
| * Given a list of usernames, verifies if user belongs to it. |
| * |
| * @param username |
| * @param usernameList |
| * @return |
| */ |
| private boolean isUsernameInList(String username, String [] usernameList) { |
| for(int i=0; i < usernameList.length; i++) { |
| if(usernameList[i].trim().equals(username)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ptp.remotetools.core.IRemoteStatusTools#getUserID() |
| */ |
| public int getUserID() throws RemoteConnectionException, RemoteOperationException, CancelException { |
| // If there's already information on cache, use it |
| if(userInfoCache == null) { |
| userInfoCache = fetchRemoteUserInfo(); |
| } |
| return userInfoCache.getUserID().intValue(); |
| } |
| |
| /** |
| * An implementation of the {@link getUserID} method that only uses the cat command on the remote machine |
| * to extract desired data. Specifically it reads information from the /etc/passwd file and parses it to |
| * retrieve the user id. |
| * |
| * @return |
| * @throws RemoteConnectionException |
| * @throws RemoteOperationException |
| * @throws CancelException |
| */ |
| public int getUserID_CatBased() throws RemoteConnectionException, RemoteOperationException, CancelException { |
| // Get the field userid from the result of the getPasswdFields method |
| // and return it. |
| String strUserID = getPasswdFields()[PASSWD_USERID_FIELD]; |
| return Integer.parseInt(strUserID); |
| } |
| |
| /** |
| * Get the line that has the user information from the passwd file |
| * |
| * @return String [] A vector containing the fields or null if no user matches |
| * @throws RemoteOperationException |
| * @throws CancelException |
| * @throws RemoteConnectionException |
| */ |
| private String [] getPasswdFields() throws RemoteOperationException, RemoteConnectionException, CancelException { |
| String currentUsername = getUsername_WhoamiBased(); |
| IRemoteExecutionTools remExecTools = manager.getExecutionTools(); |
| |
| try { |
| // Get lines from the passwd |
| String rawPasswd = remExecTools.executeWithOutput("cat /etc/passwd"); //$NON-NLS-1$ |
| String [] rawPasswdLines = rawPasswd.split("\\n"); //$NON-NLS-1$ |
| |
| // Look for the string that matches the value from the currentUsername |
| // That string is on the first line |
| for(int i=0; i < rawPasswdLines.length; i++) { |
| // Make sure it includes the last field even if it's blank. |
| String [] passwdFields = rawPasswdLines[i].split(":", -1); //$NON-NLS-1$ |
| if(passwdFields[PASSWD_USERNAME_FIELD].equals(currentUsername)) |
| return passwdFields; |
| } |
| } catch (RemoteExecutionException e) { |
| throw new RemoteOperationException(e); |
| } |
| |
| throw new RuntimeException(Messages.RemoteStatusTools_GetPasswdFields_NoUsernameInPasswdFile); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ptp.remotetools.core.IRemoteStatusTools#getUsername() |
| */ |
| public String getUsername() throws RemoteConnectionException, RemoteOperationException, |
| CancelException { |
| if(userInfoCache == null) { |
| userInfoCache = fetchRemoteUserInfo(); |
| } |
| return userInfoCache.getUsername(); |
| } |
| |
| /** |
| * * An implementation of the {@link getUsername} method that uses the whoami command on the remote machine |
| * to extract username information. |
| * |
| * @return |
| * @throws RemoteConnectionException |
| * @throws RemoteOperationException |
| * @throws CancelException |
| */ |
| private String getUsername_WhoamiBased() throws RemoteConnectionException, RemoteOperationException, |
| CancelException { |
| // Get information from the whoami command |
| try { |
| IRemoteExecutionTools remExecTools = manager.getExecutionTools(); |
| String rawWhoAmI = remExecTools.executeWithOutput("whoami"); //$NON-NLS-1$ |
| return rawWhoAmI.trim(); |
| } catch (RemoteExecutionException e) { |
| throw new RemoteOperationException(e); |
| } |
| } |
| |
| /** |
| * A method designed to parse the /etc/group file to find the list |
| * of groups that a user is enrolled in. |
| * |
| * @param username The name of the user that we are searching for enrollments. |
| * @return A list of groups that username belongs to. |
| * @throws RemoteConnectionException |
| * @throws RemoteOperationException |
| */ |
| private Set<Integer> readGroupList(String username) throws RemoteConnectionException, RemoteOperationException { |
| Set<Integer> groupList = new HashSet<Integer>(); |
| |
| BufferedReader etcGroupFile = null; |
| |
| try { |
| IRemoteFileTools ft = manager.getRemoteFileTools(); |
| InputStream is = ft.getInputStream("/etc/group",null); //$NON-NLS-1$ |
| etcGroupFile = new BufferedReader(new InputStreamReader(is )); |
| |
| String line = etcGroupFile.readLine(); |
| |
| while (line != null) { |
| String[] fields = line.split(":"); //$NON-NLS-1$ |
| |
| if (fields[0].trim().compareTo(username) == 0) { |
| groupList.add(new Integer(fields[2])); |
| } else if (fields.length == 4) { |
| |
| String[] users = fields[3].split(","); //$NON-NLS-1$ |
| for (int i = 0; i < users.length; i++) { |
| |
| if (users[i].trim().compareTo(username) == 0) { |
| groupList.add(new Integer(fields[2])); |
| } |
| } |
| } |
| |
| line = etcGroupFile.readLine(); |
| } |
| } catch (IOException ioe) { |
| throw new RemoteOperationException(ioe); |
| } catch (CancelException ce) { |
| throw new RemoteOperationException(ce); |
| } catch (NumberFormatException nfe) { |
| //Maybe the format of this /etc/group file put the fields in a |
| //different order. |
| throw new RemoteOperationException(nfe); |
| } catch (ArrayIndexOutOfBoundsException aioobe) { |
| //This case is going to happen if the format is not the expected one. |
| throw new RemoteOperationException(aioobe); |
| } |
| finally { |
| try { |
| if (etcGroupFile != null) { |
| etcGroupFile.close(); |
| } |
| } catch (IOException ioe) {throw new RemoteOperationException(ioe);} |
| } |
| return groupList; |
| } |
| |
| /** |
| * Return all the user information from the remote machine. |
| * This operation is a bit slow and should be used only once per RemoteStatusTools object. |
| * |
| * @throws RemoteConnectionException |
| * @throws RemoteOperationException |
| * @throws CancelException |
| * @return |
| */ |
| private UserInformation fetchRemoteUserInfo() throws RemoteConnectionException, RemoteOperationException, |
| CancelException { |
| try { |
| //Set of groups that this user belongs to. |
| Set<Integer> groupList; |
| |
| IRemoteExecutionTools remExecTools = manager.getExecutionTools(); |
| // Execute command to return the username, uid and the group list |
| String rawUserInfo = remExecTools.executeWithOutput("echo `id -un`:`id -u`:`id -G`"); //$NON-NLS-1$ |
| |
| String [] userInfoFields = rawUserInfo.trim().split(":", -1); //$NON-NLS-1$ |
| |
| //With systems using BusyBox, id has no argument -G |
| //so we must parse /etc/group in this case |
| if (userInfoFields[2].trim().equalsIgnoreCase("")) { //$NON-NLS-1$ |
| groupList = readGroupList(userInfoFields[0]); |
| } else { |
| String[] groupFields = userInfoFields[2].split(" ", -1); //$NON-NLS-1$ |
| |
| groupList = new HashSet<Integer>(); |
| |
| for (int i = 0; i < groupFields.length; i++) { |
| // Convert groups to Integer and put into the Set |
| groupList.add(new Integer(groupFields[i])); |
| } |
| } |
| |
| return new UserInformation(new Integer(userInfoFields[1]), groupList, userInfoFields[0]); |
| } catch (RemoteExecutionException e) { |
| throw new RemoteOperationException(e); |
| } |
| } |
| |
| public long getTime() throws RemoteConnectionException, RemoteOperationException, CancelException { |
| try { |
| IRemoteExecutionTools execTools = manager.getExecutionTools(); |
| String rawData = execTools.executeWithOutput("date +'%s'"); //$NON-NLS-1$ |
| rawData = rawData.trim(); |
| return Long.parseLong(rawData)*1000; |
| } catch (RemoteExecutionException e) { |
| throw new RemoteOperationException(e); |
| } |
| } |
| |
| } |