You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2009/11/19 14:05:19 UTC
svn commit: r882134 - in
/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd: ./
common/channel/ common/util/ server/channel/ server/shell/
Author: gnodet
Date: Thu Nov 19 13:05:18 2009
New Revision: 882134
URL: http://svn.apache.org/viewvc?rev=882134&view=rev
Log:
SSHD-63: Better support for controlling the console when launching external processes for shells
Removed:
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/util/LfToCrLfFilterOutputStream.java
Modified:
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java Thu Nov 19 13:05:18 2009
@@ -23,6 +23,7 @@
import java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -31,10 +32,16 @@
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.core.session.IoSessionConfig;
-import org.apache.mina.transport.socket.DefaultSocketSessionConfig;
-import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
-import org.apache.sshd.common.*;
+import org.apache.sshd.common.AbstractFactoryManager;
+import org.apache.sshd.common.Channel;
+import org.apache.sshd.common.Cipher;
+import org.apache.sshd.common.Compression;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.KeyExchange;
+import org.apache.sshd.common.Mac;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
import org.apache.sshd.common.cipher.AES128CBC;
import org.apache.sshd.common.cipher.AES192CBC;
import org.apache.sshd.common.cipher.AES256CBC;
@@ -43,7 +50,6 @@
import org.apache.sshd.common.compression.CompressionNone;
import org.apache.sshd.common.future.CloseFuture;
import org.apache.sshd.common.future.SshFutureListener;
-import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
import org.apache.sshd.common.mac.HMACMD5;
import org.apache.sshd.common.mac.HMACMD596;
import org.apache.sshd.common.mac.HMACSHA1;
@@ -55,13 +61,21 @@
import org.apache.sshd.common.signature.SignatureDSA;
import org.apache.sshd.common.signature.SignatureRSA;
import org.apache.sshd.common.util.SecurityUtils;
-import org.apache.sshd.server.*;
+import org.apache.sshd.server.Command;
+import org.apache.sshd.server.CommandFactory;
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.apache.sshd.server.PublickeyAuthenticator;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.TcpIpForwardFilter;
+import org.apache.sshd.server.UserAuth;
import org.apache.sshd.server.auth.UserAuthPassword;
import org.apache.sshd.server.auth.UserAuthPublicKey;
import org.apache.sshd.server.channel.ChannelDirectTcpip;
import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.kex.DHG1;
import org.apache.sshd.server.kex.DHG14;
+import org.apache.sshd.server.keyprovider.PEMGeneratorHostKeyProvider;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.session.SessionFactory;
import org.apache.sshd.server.shell.ProcessShellFactory;
@@ -266,6 +280,10 @@
* Stop the SSH server. This method will block until all resources are actually disposed.
*/
public void stop() throws InterruptedException {
+ stop(false);
+ }
+
+ public void stop(boolean immediately) throws InterruptedException {
acceptor.setCloseOnDeactivation(false);
acceptor.unbind();
List<AbstractSession> sessions = new ArrayList<AbstractSession>();
@@ -282,9 +300,11 @@
}
};
for (AbstractSession session : sessions) {
- session.close(false).addListener(listener);
+ session.close(immediately).addListener(listener);
+ }
+ if (!immediately) {
+ latch.await();
}
- latch.await();
acceptor.dispose();
acceptor = null;
}
@@ -399,12 +419,22 @@
System.exit(-1);
}
+ System.err.println("Starting SSHD on port " + port);
+
SshServer sshd = SshServer.setUpDefaultServer();
sshd.setPort(port);
- sshd.setKeyPairProvider(new FileKeyPairProvider(new String[] { "/etc/ssh_host_rsa_key", "/etc/ssh_host_dsa_key" }));
- //sshd.setShellFactory(new ProcessShellFactory(new String[] { "/usr/bin/login", "-f", "-h", "localhost", "$USER", "/bin/sh", "-i" }));
- sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", "-l" }));
- //sshd.setPasswordAuthenticator(new PAMPasswordAuthenticator());
+ if (SecurityUtils.isBouncyCastleRegistered()) {
+ sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider("key.pem"));
+ } else {
+ sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider("key.ser"));
+ }
+ if (System.getProperty("os.name").toLowerCase().indexOf("windows") < 0) {
+ sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", "-l" },
+ EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr)));
+ } else {
+ sshd.setShellFactory(new ProcessShellFactory(new String[] { "cmd.exe "},
+ EnumSet.of(ProcessShellFactory.TtyOptions.Echo, ProcessShellFactory.TtyOptions.ICrNl, ProcessShellFactory.TtyOptions.ONlCr)));
+ }
sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
public boolean authenticate(String username, String password, ServerSession session) {
return username != null && username.equals(password);
Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelPipedInputStream.java Thu Nov 19 13:05:18 2009
@@ -46,6 +46,13 @@
this.localWindow = localWindow;
}
+ @Override
+ public int available() throws IOException {
+ synchronized (buffer) {
+ return buffer.available();
+ }
+ }
+
public int read() throws IOException {
synchronized (b) {
int l = read(b, 0, 1);
Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/channel/ChannelSession.java Thu Nov 19 13:05:18 2009
@@ -40,10 +40,13 @@
import org.apache.sshd.common.future.SshFutureListener;
import org.apache.sshd.common.util.Buffer;
import org.apache.sshd.common.util.IoUtils;
-import org.apache.sshd.common.util.LfToCrLfFilterOutputStream;
import org.apache.sshd.common.util.LoggingFilterOutputStream;
-import org.apache.sshd.server.*;
+import org.apache.sshd.server.Command;
import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.SessionAware;
+import org.apache.sshd.server.Signal;
+import org.apache.sshd.server.SignalListener;
import org.apache.sshd.server.session.ServerSession;
/**
@@ -424,10 +427,6 @@
// Wrap in logging filters
out = new LoggingFilterOutputStream(out, "OUT:", log);
err = new LoggingFilterOutputStream(err, "ERR:", log);
- if (getPtyModeValue(PtyMode.ONLCR) != 0) {
- out = new LfToCrLfFilterOutputStream(out);
- err = new LfToCrLfFilterOutputStream(err);
- }
in = new ChannelPipedInputStream(localWindow);
shellIn = new ChannelPipedOutputStream((ChannelPipedInputStream) in);
shellIn = new LoggingFilterOutputStream(shellIn, "IN: ", log);
Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java?rev=882134&r1=882133&r2=882134&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/server/shell/ProcessShellFactory.java Thu Nov 19 13:05:18 2009
@@ -18,33 +18,51 @@
*/
package org.apache.sshd.server.shell;
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.EnumSet;
import java.util.Map;
import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.util.Buffer;
import org.apache.sshd.server.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * A {@link ShellFactory} that will create a new process and bridge
+ * A {@link Factory} of {@link Command} that will create a new process and bridge
* the streams.
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class ProcessShellFactory implements Factory<Command> {
+ public enum TtyOptions {
+ Echo,
+ INlCr,
+ ICrNl,
+ ONlCr,
+ OCrNl
+ }
+
private static final Logger LOG = LoggerFactory.getLogger(ProcessShellFactory.class);
private String[] command;
+ private EnumSet<TtyOptions> ttyOptions;
public ProcessShellFactory() {
}
public ProcessShellFactory(String[] command) {
+ this(command, EnumSet.noneOf(TtyOptions.class));
+ }
+
+ public ProcessShellFactory(String[] command, EnumSet<TtyOptions> ttyOptions) {
this.command = command;
+ this.ttyOptions = ttyOptions;
}
public String[] getCommand() {
@@ -56,17 +74,15 @@
}
public Command create() {
- return new InvertedShellWrapper(new ProcessShell(command));
+ return new InvertedShellWrapper(new ProcessShell());
}
- public static class ProcessShell implements InvertedShell {
+ public class ProcessShell implements InvertedShell {
- private String[] command;
private Process process;
-
- public ProcessShell(String[] command) {
- this.command = command;
- }
+ private TtyFilterOutputStream in;
+ private TtyFilterInputStream out;
+ private TtyFilterInputStream err;
public void start(Map<String,String> env) throws IOException {
String[] cmds = new String[command.length];
@@ -83,18 +99,21 @@
}
LOG.info("Starting shell with command: '{}' and env: {}", builder.command(), builder.environment());
process = builder.start();
+ out = new TtyFilterInputStream(process.getInputStream());
+ err = new TtyFilterInputStream(process.getErrorStream());
+ in = new TtyFilterOutputStream(process.getOutputStream(), err);
}
public OutputStream getInputStream() {
- return process.getOutputStream();
+ return in;
}
public InputStream getOutputStream() {
- return process.getInputStream();
+ return out;
}
public InputStream getErrorStream() {
- return process.getErrorStream();
+ return err;
}
public boolean isAlive() {
@@ -113,6 +132,84 @@
public void destroy() {
process.destroy();
}
+
+ protected class TtyFilterInputStream extends FilterInputStream {
+ private Buffer buffer;
+ private int lastChar;
+ public TtyFilterInputStream(InputStream in) {
+ super(in);
+ buffer = new Buffer(32);
+ }
+ synchronized void write(int c) {
+ buffer.putByte((byte) c);
+ }
+ synchronized void write(byte[] buf, int off, int len) {
+ buffer.putBytes(buf, off, len);
+ }
+ @Override
+ public int available() throws IOException {
+ return super.available() + buffer.available();
+ }
+ @Override
+ public synchronized int read() throws IOException {
+ int c;
+ if (buffer.available() > 0) {
+ c = buffer.getByte();
+ buffer.compact();
+ } else {
+ c = super.read();
+ }
+ if (c == '\n' && ttyOptions.contains(TtyOptions.ONlCr) && lastChar != '\r') {
+ c = '\r';
+ Buffer buf = new Buffer();
+ buf.putByte((byte) '\n');
+ buf.putBuffer(buffer);
+ buffer = buf;
+ } else if (c == '\r' && ttyOptions.contains(TtyOptions.OCrNl)) {
+ c = '\n';
+ }
+ lastChar = c;
+ return c;
+ }
+ @Override
+ public synchronized int read(byte[] b, int off, int len) throws IOException {
+ if (buffer.available() == 0) {
+ int nb = super.read(b, off, len);
+ buffer.putRawBytes(b, off, nb);
+ }
+ int nb = 0;
+ while (nb < len && buffer.available() > 0) {
+ b[off + nb++] = (byte) read();
+ }
+ return nb;
+ }
+ }
+
+ protected class TtyFilterOutputStream extends FilterOutputStream {
+ private TtyFilterInputStream echo;
+ public TtyFilterOutputStream(OutputStream out, TtyFilterInputStream echo) {
+ super(out);
+ this.echo = echo;
+ }
+ @Override
+ public void write(int c) throws IOException {
+ if (c == '\n' && ttyOptions.contains(TtyOptions.INlCr)) {
+ c = '\r';
+ } else if (c == '\r' && ttyOptions.contains(TtyOptions.ICrNl)) {
+ c = '\n';
+ }
+ super.write(c);
+ if (ttyOptions.contains(TtyOptions.Echo)) {
+ echo.write(c);
+ }
+ }
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ for (int i = off; i < len; i++) {
+ write(b[i]);
+ }
+ }
+ }
}
}