diff --git a/remote-ssh/pom.xml b/remote-ssh/pom.xml index 463cc975d..e185976ae 100644 --- a/remote-ssh/pom.xml +++ b/remote-ssh/pom.xml @@ -55,6 +55,12 @@ junit-jupiter-api test + + + org.slf4j + slf4j-jdk14 + test + diff --git a/remote-ssh/src/main/java/org/jline/builtins/ssh/ShellFactoryImpl.java b/remote-ssh/src/main/java/org/jline/builtins/ssh/ShellFactoryImpl.java index 05582dcdc..603239f8c 100644 --- a/remote-ssh/src/main/java/org/jline/builtins/ssh/ShellFactoryImpl.java +++ b/remote-ssh/src/main/java/org/jline/builtins/ssh/ShellFactoryImpl.java @@ -30,38 +30,23 @@ import org.jline.terminal.Size; import org.jline.terminal.Terminal; import org.jline.terminal.TerminalBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * SSHD {@link org.apache.sshd.server.command.Command} factory which provides access to * Shell. */ public class ShellFactoryImpl implements ShellFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(ShellFactoryImpl.class); + private final Consumer shell; public ShellFactoryImpl(Consumer shell) { this.shell = shell; } - private static void flush(OutputStream... streams) { - for (OutputStream s : streams) { - try { - s.flush(); - } catch (IOException e) { - // Ignore - } - } - } - - static void close(Closeable... closeables) { - for (Closeable c : closeables) { - try { - c.close(); - } catch (IOException e) { - // Ignore - } - } - } - public Command createShell(ChannelSession session) { return new ShellImpl(); } @@ -95,20 +80,13 @@ public void setExitCallback(ExitCallback callback) { public void start(final ChannelSession session, final Environment env) throws IOException { try { - new Thread(() -> { - try { - ShellImpl.this.run(session, env); - } catch (Throwable t) { - t.printStackTrace(); - } - }) - .start(); + new Thread(() -> ShellImpl.this.run(session, env)).start(); } catch (Exception e) { throw new IOException("Unable to start shell", e); } } - public void run(ChannelSession session, Environment env) throws Exception { + public void run(ChannelSession session, Environment env) { try { Attributes attributes = new Attributes(); for (Map.Entry e : env.getPtyModes().entrySet()) { @@ -222,7 +200,9 @@ public void run(ChannelSession session, Environment env) throws Exception { shell.accept(new Ssh.ShellParams(env.getEnv(), session.getSession(), terminal, () -> destroy(session))); } catch (Throwable t) { - t.printStackTrace(); + if (!closed) { + LOGGER.error("Error occured while executing shell", t); + } } } @@ -235,4 +215,24 @@ public void destroy(ChannelSession session) { } } } + + static void flush(OutputStream... streams) { + for (OutputStream s : streams) { + try { + s.flush(); + } catch (IOException e) { + LOGGER.debug("Error flushing " + s, e); + } + } + } + + static void close(Closeable... closeables) { + for (Closeable c : closeables) { + try { + c.close(); + } catch (IOException e) { + LOGGER.debug("Error closing " + c, e); + } + } + } } diff --git a/remote-ssh/src/test/java/org/jline/builtins/ssh/SshdTest.java b/remote-ssh/src/test/java/org/jline/builtins/ssh/SshdTest.java new file mode 100644 index 000000000..c563a09ca --- /dev/null +++ b/remote-ssh/src/test/java/org/jline/builtins/ssh/SshdTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002-2024, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.builtins.ssh; + +import java.nio.file.Paths; + +import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.channel.ChannelShell; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.server.SshServer; +import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; +import org.jline.reader.EndOfFileException; +import org.jline.reader.LineReader; +import org.jline.reader.LineReaderBuilder; +import org.jline.reader.UserInterruptException; +import org.junit.jupiter.api.Test; + +public class SshdTest { + + @Test + void test() throws Exception { + SshServer sshd = SshServer.setUpDefaultServer(); + sshd.setPort(0); + sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(Paths.get("target/hostkey.ser"))); + sshd.setPasswordAuthenticator((username, password, session) -> true); + sshd.setShellFactory(new ShellFactoryImpl(shellParams -> { + LineReader reader = LineReaderBuilder.builder() + .terminal(shellParams.getTerminal()) + .build(); + + try { + String line; + reader.printAbove("Welcome to SSH Server"); + while ((line = reader.readLine("sshTest > ")) != null) { + System.out.println(line); + } + } catch (UserInterruptException e) { + // Ignore + } catch (EndOfFileException e) { + // Ignore + } catch (Exception e) { + // ignore OTHER EXCEPTIONS + } + })); + + // Start the server + sshd.start(); + System.out.println("SSH Server started on port 2222"); + + SshClient client = SshClient.setUpDefaultClient(); + client.start(); + ClientSession session = + client.connect("test", "localhost", sshd.getPort()).verify().getClientSession(); + session.addPasswordIdentity("foo"); + session.auth().verify(); + ChannelShell shell = session.createShellChannel(); + shell.open().verify(); + shell.getInvertedIn().write("echo foo\n".getBytes()); + shell.close(); + client.close(); + + Thread.sleep(1000); + } +}