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);
+ }
+}