/* * Copyright 2008-2012 Amazon Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://aws.amazon.com/apache2.0 * * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES * OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and * limitations under the License. */ package com.amazonaws.eclipse.ec2; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.util.logging.Logger; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.ui.statushandlers.StatusManager; import com.amazonaws.eclipse.ec2.preferences.PreferenceConstants; /** * Basic utilities for working with platform differences. */ public class PlatformUtils { private static final String PPK_CONVERTER_EXE = "/bin/PemToPPKConverter.exe"; private static final Logger logger = Logger.getLogger(PlatformUtils.class.getName()); /** * Returns true if the current platform is a windows platform. * * @return True if the current platform is a windows platform. */ public boolean isWindows() { String platform = System.getProperty("os.name"); if (platform == null) { Status status = new Status(IStatus.WARNING, Ec2Plugin.PLUGIN_ID, "No system property for 'os.name'"); StatusManager.getManager().handle(status, StatusManager.LOG); return false; } return platform.toLowerCase().contains("windows"); } /** * Returns true if the current platform is a Linux platform. * * @return True if the current platform is a Linux platform. */ public boolean isLinux() { String platform = System.getProperty("os.name"); if (platform == null) { Status status = new Status(IStatus.WARNING, Ec2Plugin.PLUGIN_ID, "No system property for 'os.name'"); StatusManager.getManager().handle(status, StatusManager.LOG); return false; } return platform.toLowerCase().contains("linux"); } /** * Returns true if the current platform is a Mac platform. * * @return True if the current platform is a Mac platform. */ public boolean isMac() { String platform = System.getProperty("os.name"); if (platform == null) { Status status = new Status(IStatus.WARNING, Ec2Plugin.PLUGIN_ID, "No system property for 'os.name'"); StatusManager.getManager().handle(status, StatusManager.LOG); return false; } return platform.toLowerCase().contains("mac os"); } /** * Returns true if the platform specific SSH client is correctly configured * and ready to be used on this system. * * @return True if the platform specific SSH client is correctly configured * and ready to be used on this system. */ public boolean isSshClientConfigured() { if (isWindows()) { String puttyPath = Ec2Plugin.getDefault().getPreferenceStore().getString(PreferenceConstants.P_PUTTY_EXECUTABLE); // First make sure something is specified... if (puttyPath == null || puttyPath.length() == 0) return false; // Next make sure it's a file if (! new File(puttyPath).isFile()) return false; } return true; } /** * Opens a shell to the specified host using a platform specific terminal * window. * * @param user * The user to connect to the remote host as. * @param host * The remote host to connect to. * @param identityFile * The file containing the identity file for the connection. * @throws IOException * If any problems are encountered opening the remote shell. */ public void openShellToRemoteHost(String user, String host, String identityFile) throws IOException, InterruptedException, URISyntaxException { IPreferenceStore preferenceStore = Ec2Plugin.getDefault().getPreferenceStore(); String sshOptions = preferenceStore.getString(PreferenceConstants.P_SSH_OPTIONS); String sshCommand = preferenceStore.getString(PreferenceConstants.P_SSH_CLIENT); sshCommand += " " + sshOptions + " -i " + identityFile + " " + user + "@" + host; if (isMac()) { URL locationUrl = FileLocator.find(Ec2Plugin.getDefault().getBundle(),new Path("/"), null); URL fileUrl = FileLocator.toFileURL(locationUrl); executeAsynchronousCommand(new String[] {"osascript", fileUrl.getFile() + "scripts/openMacTerminalShell.scpt", sshCommand}); } else if (isLinux()) { String terminalCommand = preferenceStore.getString(PreferenceConstants.P_TERMINAL_EXECUTABLE); executeAsynchronousCommand(new String[] {terminalCommand, "-e", sshCommand}); } else if (isWindows()) { openRemoteShellFromWindows(user, host, identityFile); } else { String osName = System.getProperty("os.name"); Status status = new Status(IStatus.ERROR, Ec2Plugin.PLUGIN_ID, "Unable to determine what platform '" + osName + "' is."); StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG); } } /* * Private Interface */ /** * Opens a remote shell connection from a windows host as the specified user * to the specified host using the OpenSSH identity file to authenticate. * This method assumes that the user has already configured their SSH tools * (PuTTY), so it doesn't check for that, but it does check to see if the * required PuTTY private key exists, and if it doesn't, it will convert it. * * @param user * The user to log into the remote host as. * @param host * The host to connect to. * @param identityFile * The OpenSSH private key file that provides the user * passwordless access to the specified host. * @throws IOException * If any problems are encountered executing the SSH client. */ private void openRemoteShellFromWindows(String user, String host, String identityFile) throws IOException, InterruptedException, URISyntaxException { String puttyExecutable = Ec2Plugin.getDefault().getPreferenceStore().getString( PreferenceConstants.P_PUTTY_EXECUTABLE); File privateKeyFile = new File(identityFile); if (!privateKeyFile.isFile()) { throw new IOException("Unable to find the required OpenSSH private key '" + identityFile + "'."); } String puttyPrivateKeyFile = translateOpenSshPrivateKeyFileToPuttyPrivateKeyFile(identityFile); File ppkFile = new File(puttyPrivateKeyFile); if (! ppkFile.exists()) { executeAsynchronousCommand(new String[] {getPuttyGenConversionExecutable(), "\"" + identityFile + "\"", "\"" + ppkFile.getAbsolutePath() + "\""}).waitFor(); } String[] openShellCommand = new String[] {puttyExecutable, "-ssh", "-i", puttyPrivateKeyFile, user + "@" + host}; executeAsynchronousCommand(openShellCommand); } /** * Returns the path to the bundled puttygen conversion utility * @throws IOException */ private String getPuttyGenConversionExecutable() throws URISyntaxException, IOException { URL conversionExe = FileLocator.resolve(FileLocator.find(Ec2Plugin.getDefault().getBundle(), new Path(PPK_CONVERTER_EXE), null)); return new File(conversionExe.toURI()).getAbsolutePath(); } /** * Translates the full path to an OpenSSH private key file to a full path * for the corresponding PuTTY private key file. * * @param identityFile * The full path to an OpenSSH private key file. * * @return The full path for the corresponding PuTTY private key. * * @throws IOException * If any problems were encountered translating the OpenSSH * private key file path. */ private String translateOpenSshPrivateKeyFileToPuttyPrivateKeyFile(String identityFile) throws IOException { int suffixIndex = identityFile.lastIndexOf("."); if (suffixIndex < 0) { throw new IOException("Unable to translate '" + identityFile + "' to a PuTTY private key file path."); } String puttyPrivateKeyFile = identityFile.substring(0, suffixIndex); puttyPrivateKeyFile = puttyPrivateKeyFile + ".ppk"; return puttyPrivateKeyFile; } /** * Executes the specified command array, but does NOT wait for it to finish, * therefore no exit code is returned. * * @param commandArray * The command array to execute. * @throws IOException * If there were any problems kicking off the command. */ public Process executeAsynchronousCommand(String[] commandArray) throws IOException { String commandString = ""; for (String command : commandArray) { commandString += command + " "; } logger.info("Executing: " + commandString); return Runtime.getRuntime().exec(commandArray); } }