From: Mathieu Baudier Date: Sun, 8 Sep 2019 16:43:53 +0000 (+0200) Subject: Simplify SFTP usage. X-Git-Tag: argeo-commons-2.1.79~9 X-Git-Url: https://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=c718f8be43b6ee3b1ba111c2a22c9037e35b872f Simplify SFTP usage. --- diff --git a/org.argeo.core/src/org/argeo/ssh/AbstractSsh.java b/org.argeo.core/src/org/argeo/ssh/AbstractSsh.java index 588c16b01..261ac2460 100644 --- a/org.argeo.core/src/org/argeo/ssh/AbstractSsh.java +++ b/org.argeo.core/src/org/argeo/ssh/AbstractSsh.java @@ -3,16 +3,24 @@ package org.argeo.ssh; import java.io.Console; import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.util.Arrays; +import java.util.HashSet; import java.util.Scanner; +import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.sshd.client.SshClient; +import org.apache.sshd.client.channel.ClientChannel; +import org.apache.sshd.client.channel.ClientChannelEvent; import org.apache.sshd.client.future.ConnectFuture; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.client.subsystem.sftp.fs.SftpFileSystemProvider; +import org.apache.sshd.common.util.io.NoCloseInputStream; +import org.apache.sshd.common.util.io.NoCloseOutputStream; +@SuppressWarnings("restriction") abstract class AbstractSsh { private final static Log log = LogFactory.getLog(AbstractSsh.class); @@ -44,7 +52,6 @@ abstract class AbstractSsh { return sftpFileSystemProvider; } - @SuppressWarnings("restriction") void authenticate() { try { if (sshKeyPair != null) { @@ -97,7 +104,6 @@ abstract class AbstractSsh { openSession(uri.getUserInfo(), uri.getHost(), uri.getPort() > 0 ? uri.getPort() : null); } - @SuppressWarnings("restriction") void openSession(String login, String host, Integer port) { if (session != null) throw new IllegalStateException("Session is already open"); @@ -131,7 +137,6 @@ abstract class AbstractSsh { } } - @SuppressWarnings("restriction") void closeSession() { if (session == null) throw new IllegalStateException("No session is open"); @@ -148,4 +153,37 @@ abstract class AbstractSsh { return session; } + public void setSshKeyPair(SshKeyPair sshKeyPair) { + this.sshKeyPair = sshKeyPair; + } + + public static void openShell(ClientSession session) { + try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) { + channel.setIn(new NoCloseInputStream(System.in)); + channel.setOut(new NoCloseOutputStream(System.out)); + channel.setErr(new NoCloseOutputStream(System.err)); + channel.open(); + + Set events = new HashSet<>(); + events.add(ClientChannelEvent.CLOSED); + channel.waitFor(events, 0); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + session.close(false); + } + } + + static URI toUri(String username, String host, int port) { + try { + if (username == null) + username = "root"; + return new URI("ssh://" + username + "@" + host + ":" + port); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Cannot generate SSH URI to " + host + ":" + port + " for " + username, + e); + } + } + } diff --git a/org.argeo.core/src/org/argeo/ssh/BasicSshServer.java b/org.argeo.core/src/org/argeo/ssh/BasicSshServer.java new file mode 100644 index 000000000..3333c7fb8 --- /dev/null +++ b/org.argeo.core/src/org/argeo/ssh/BasicSshServer.java @@ -0,0 +1,103 @@ +package org.argeo.ssh; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.sshd.server.SshServer; +import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; +import org.apache.sshd.server.scp.ScpCommandFactory; +import org.apache.sshd.server.shell.ProcessShellFactory; +import org.argeo.util.os.OS; + +/** A simple SSH server with some defaults. Supports SCP. */ +@SuppressWarnings("restriction") +public class BasicSshServer { + private Integer port; + private Path hostKeyPath; + + private SshServer sshd = null; + + public BasicSshServer(Integer port, Path hostKeyPath) { + this.port = port; + this.hostKeyPath = hostKeyPath; + } + + public void init() { + try { + sshd = SshServer.setUpDefaultServer(); + sshd.setPort(port); + if (hostKeyPath == null) + throw new IllegalStateException("An SSH server key must be set"); + sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKeyPath)); + // sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", + // "-l" })); + String[] shellCommand = OS.LOCAL.getDefaultShellCommand(); + sshd.setShellFactory(new ProcessShellFactory(shellCommand)); + sshd.setCommandFactory(new ScpCommandFactory()); + sshd.start(); + } catch (Exception e) { + throw new RuntimeException("Cannot start SSH server on port " + port, e); + } + } + + public void destroy() { + try { + sshd.stop(); + } catch (IOException e) { + throw new RuntimeException("Cannot stop SSH server on port " + port, e); + } + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Path getHostKeyPath() { + return hostKeyPath; + } + + public void setHostKeyPath(Path hostKeyPath) { + this.hostKeyPath = hostKeyPath; + } + + public static void main(String[] args) { + int port = 2222; + Path hostKeyPath = Paths.get("hostkey.ser"); + try { + if (args.length > 0) + port = Integer.parseInt(args[0]); + if (args.length > 1) + hostKeyPath = Paths.get(args[1]); + } catch (Exception e1) { + printUsage(); + } + + BasicSshServer sshServer = new BasicSshServer(port, hostKeyPath); + sshServer.init(); + Runtime.getRuntime().addShutdownHook(new Thread("Shutdown SSH server") { + + @Override + public void run() { + sshServer.destroy(); + } + }); + try { + synchronized (sshServer) { + sshServer.wait(); + } + } catch (InterruptedException e) { + sshServer.destroy(); + } + + } + + public static void printUsage() { + System.out.println("java " + BasicSshServer.class.getName() + " [port] [server key path]"); + } + +} diff --git a/org.argeo.core/src/org/argeo/ssh/Sftp.java b/org.argeo.core/src/org/argeo/ssh/Sftp.java index df4dd3a01..da10b961f 100644 --- a/org.argeo.core/src/org/argeo/ssh/Sftp.java +++ b/org.argeo.core/src/org/argeo/ssh/Sftp.java @@ -7,11 +7,16 @@ import java.nio.file.Path; import org.apache.sshd.client.subsystem.sftp.fs.SftpFileSystem; +/** Create an SFTP {@link FileSystem}. */ public class Sftp extends AbstractSsh { private URI uri; private SftpFileSystem fileSystem; + public Sftp(String username, String host, int port) { + this(AbstractSsh.toUri(username, host, port)); + } + public Sftp(URI uri) { this.uri = uri; openSession(uri); diff --git a/org.argeo.core/src/org/argeo/ssh/SimpleSshServer.java b/org.argeo.core/src/org/argeo/ssh/SimpleSshServer.java deleted file mode 100644 index e63b989a4..000000000 --- a/org.argeo.core/src/org/argeo/ssh/SimpleSshServer.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.argeo.ssh; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.apache.sshd.server.SshServer; -import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; -import org.apache.sshd.server.scp.ScpCommandFactory; -import org.apache.sshd.server.shell.ProcessShellFactory; -import org.argeo.util.os.OS; - -/** A simple SSH server with some defaults. Supports SCP. */ -@SuppressWarnings("restriction") -public class SimpleSshServer { - private Integer port; - private Path hostKeyPath; - - private SshServer sshd = null; - - public SimpleSshServer(Integer port, Path hostKeyPath) { - this.port = port; - this.hostKeyPath = hostKeyPath; - } - - public void init() { - try { - sshd = SshServer.setUpDefaultServer(); - sshd.setPort(port); - if (hostKeyPath == null) - throw new IllegalStateException("An SSH server key must be set"); - sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKeyPath)); - // sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", - // "-l" })); - String[] shellCommand = OS.LOCAL.getDefaultShellCommand(); - sshd.setShellFactory(new ProcessShellFactory(shellCommand)); - sshd.setCommandFactory(new ScpCommandFactory()); - sshd.start(); - } catch (Exception e) { - throw new RuntimeException("Cannot start SSH server on port " + port, e); - } - } - - public void destroy() { - try { - sshd.stop(); - } catch (IOException e) { - throw new RuntimeException("Cannot stop SSH server on port " + port, e); - } - } - - public Integer getPort() { - return port; - } - - public void setPort(Integer port) { - this.port = port; - } - - public Path getHostKeyPath() { - return hostKeyPath; - } - - public void setHostKeyPath(Path hostKeyPath) { - this.hostKeyPath = hostKeyPath; - } - - public static void main(String[] args) { - int port = 2222; - Path hostKeyPath = Paths.get("hostkey.ser"); - try { - if (args.length > 0) - port = Integer.parseInt(args[0]); - if (args.length > 1) - hostKeyPath = Paths.get(args[1]); - } catch (Exception e1) { - printUsage(); - } - - SimpleSshServer sshServer = new SimpleSshServer(port, hostKeyPath); - sshServer.init(); - Runtime.getRuntime().addShutdownHook(new Thread("Shutdown SSH server") { - - @Override - public void run() { - sshServer.destroy(); - } - }); - try { - synchronized (sshServer) { - sshServer.wait(); - } - } catch (InterruptedException e) { - sshServer.destroy(); - } - - } - - public static void printUsage() { - System.out.println("java " + SimpleSshServer.class.getName() + " [port] [server key path]"); - } - -} diff --git a/org.argeo.core/src/org/argeo/ssh/Ssh.java b/org.argeo.core/src/org/argeo/ssh/Ssh.java index 044309877..68dd912ec 100644 --- a/org.argeo.core/src/org/argeo/ssh/Ssh.java +++ b/org.argeo.core/src/org/argeo/ssh/Ssh.java @@ -1,11 +1,8 @@ package org.argeo.ssh; -import java.io.IOException; import java.net.URI; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; @@ -13,39 +10,20 @@ import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.apache.sshd.client.channel.ClientChannel; -import org.apache.sshd.client.channel.ClientChannelEvent; -import org.apache.sshd.client.session.ClientSession; -import org.apache.sshd.common.util.io.NoCloseInputStream; -import org.apache.sshd.common.util.io.NoCloseOutputStream; -@SuppressWarnings("restriction") +/** Create an SSH shell. */ public class Ssh extends AbstractSsh { private final URI uri; + public Ssh(String username, String host, int port) { + this(AbstractSsh.toUri(username, host, port)); + } + public Ssh(URI uri) { this.uri = uri; openSession(uri); } - static void openShell(ClientSession session) { - try (ClientChannel channel = session.createChannel(ClientChannel.CHANNEL_SHELL)) { - channel.setIn(new NoCloseInputStream(System.in)); - channel.setOut(new NoCloseOutputStream(System.out)); - channel.setErr(new NoCloseOutputStream(System.err)); - channel.open(); - - Set events = new HashSet<>(); - events.add(ClientChannelEvent.CLOSED); - channel.waitFor(events, 0); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } finally { - session.close(false); - } - } - public static void main(String[] args) { Options options = getOptions(); CommandLineParser parser = new DefaultParser(); @@ -70,7 +48,7 @@ public class Ssh extends AbstractSsh { ssh.authenticate(); if (command.size() == 0) {// shell - openShell(ssh.getSession()); + AbstractSsh.openShell(ssh.getSession()); } else {// execute command }