1 /******************************************************************************* 2 * Copyright (c) 2011, 2017 SAP AG and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Lazar Kirchev, SAP AG - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.equinox.console.ssh; 15 16 import java.io.Closeable; 17 import java.io.IOException; 18 import java.io.InputStream; 19 import java.io.OutputStream; 20 import java.io.PrintStream; 21 import java.util.Map; 22 23 import org.apache.felix.service.command.CommandProcessor; 24 import org.apache.felix.service.command.CommandSession; 25 import org.eclipse.equinox.console.common.ConsoleInputHandler; 26 import org.eclipse.equinox.console.common.ConsoleInputScanner; 27 import org.eclipse.equinox.console.common.ConsoleInputStream; 28 import org.eclipse.equinox.console.common.ConsoleOutputStream; 29 import org.eclipse.equinox.console.common.KEYS; 30 import org.eclipse.equinox.console.common.terminal.TerminalTypeMappings; 31 import org.eclipse.equinox.console.storage.SecureUserStore; 32 import org.osgi.framework.BundleContext; 33 34 /** 35 * This class manages a ssh connection. It is responsible for wrapping the original io streams 36 * from the socket, and starting a CommandSession to execute commands from the ssh. 37 * 38 */ 39 public class SshSession extends Thread implements Closeable { 40 private CommandProcessor processor; 41 private BundleContext context; 42 private SshShell sshShell; 43 private InputStream in; 44 private OutputStream out; 45 private TerminalTypeMappings currentMappings; 46 private Map<String, KEYS> currentEscapesToKey; 47 48 private static final String PROMPT = "prompt"; 49 private static final String OSGI_PROMPT = "osgi> "; 50 private static final String SCOPE = "SCOPE"; 51 private static final String EQUINOX_SCOPE = "equinox:*"; 52 private static final String INPUT_SCANNER = "INPUT_SCANNER"; 53 private static final String SSH_INPUT_SCANNER = "SSH_INPUT_SCANNER"; 54 private static final String USER_STORAGE_PROPERTY_NAME = "osgi.console.ssh.useDefaultSecureStorage"; 55 private static final String DEFAULT_USER = "equinox"; 56 private static final String CLOSEABLE = "CLOSEABLE"; 57 private static final int ADD_USER_COUNTER_LIMIT = 2; 58 SshSession(CommandProcessor processor, BundleContext context, SshShell sshShell, InputStream in, OutputStream out, TerminalTypeMappings currentMappings, Map<String, KEYS> currentExcapesToKey)59 public SshSession(CommandProcessor processor, BundleContext context, SshShell sshShell, InputStream in, OutputStream out, TerminalTypeMappings currentMappings, Map<String, KEYS> currentExcapesToKey) { 60 this.processor = processor; 61 this.context = context; 62 this.sshShell = sshShell; 63 this.in = in; 64 this.out = out; 65 this.currentMappings = currentMappings; 66 this.currentEscapesToKey = currentExcapesToKey; 67 } 68 69 @Override run()70 public void run() { 71 ConsoleInputStream input = new ConsoleInputStream(); 72 ConsoleOutputStream outp = new ConsoleOutputStream(out); 73 SshInputHandler inputHandler = new SshInputHandler(in, input, outp); 74 inputHandler.getScanner().setBackspace(currentMappings.getBackspace()); 75 inputHandler.getScanner().setDel(currentMappings.getDel()); 76 inputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey); 77 inputHandler.getScanner().setEscapes(currentMappings.getEscapes()); 78 inputHandler.start(); 79 80 ConsoleInputStream inp = new ConsoleInputStream(); 81 ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(input, inp, outp); 82 consoleInputHandler.getScanner().setBackspace(currentMappings.getBackspace()); 83 consoleInputHandler.getScanner().setDel(currentMappings.getDel()); 84 consoleInputHandler.getScanner().setCurrentEscapesToKey(currentEscapesToKey); 85 consoleInputHandler.getScanner().setEscapes(currentMappings.getEscapes()); 86 ((ConsoleInputScanner)consoleInputHandler.getScanner()).setContext(context); 87 consoleInputHandler.start(); 88 89 final PrintStream output = new PrintStream(outp); 90 91 try (CommandSession session = processor.createSession(inp, output, output)) { 92 session.put(SCOPE, EQUINOX_SCOPE); 93 session.put(PROMPT, OSGI_PROMPT); 94 session.put(INPUT_SCANNER, consoleInputHandler.getScanner()); 95 session.put(SSH_INPUT_SCANNER, inputHandler.getScanner()); 96 // Store this closeable object in the session, so that the disconnect command 97 // can close it 98 session.put(CLOSEABLE, this); 99 ((ConsoleInputScanner) consoleInputHandler.getScanner()).setSession(session); 100 101 if ("true".equals(context.getProperty(USER_STORAGE_PROPERTY_NAME))) { 102 String[] names = SecureUserStore.getUserNames(); 103 for (String name : names) { 104 // if the default user is the only user, request creation of a new user and 105 // delete the default 106 if (DEFAULT_USER.equals(name)) { 107 if (names.length == 1) { 108 session.getConsole().println( 109 "Currently the default user is the only one; since it will be deleted after first login, create a new user:"); 110 boolean isUserAdded = false; 111 int count = 0; 112 while (!isUserAdded && count < ADD_USER_COUNTER_LIMIT) { 113 isUserAdded = ((Boolean) session.execute("addUser")).booleanValue(); 114 count++; 115 } 116 if (!isUserAdded) { 117 break; 118 } 119 } 120 if (SecureUserStore.existsUser(name)) { 121 SecureUserStore.deleteUser(name); 122 } 123 break; 124 } 125 } 126 } 127 session.execute("gosh --login --noshutdown"); 128 } catch (Exception e) { 129 e.printStackTrace(); 130 } 131 132 } 133 134 @Override close()135 public void close() throws IOException { 136 this.interrupt(); 137 sshShell.removeSession(this); 138 } 139 140 } 141