I've just done a real quick prototype to plug into smx kernel and I've
been able to log in into smx kernel using an ssh client and issue a
few commands.
Following is the class that does everything and the spring config to
start the ssh server.
The BogusPasswordAuthenticator is a dummy class which I pasted below
too.
Notice the use of stream filters to convert CR / CRLF stuff. I think
this is because both sshd and the geronimo gshell do not handle well
the pty request and/or VT100 stuff. But I'm still not sure where the
conversion should happen exactly.
Also note that i've redefined the ConsoleErrorHandlerImpl, because the
default one uses the application.getIO() for displaying errors so they
are not available remotely.
Let me know what you think, but it basically makes the whole remote
bits of gshell unused.
I have not implemented the ssh command which should be easy using
jsch lib.
Let me know if / how I can help you with that bits.
==================================================
package org.apache.servicemix.kernel.gshell.core.sshd;
import com.google.code.sshd.PasswordAuthenticator;
public class BogusPasswordAuthenticator implements
PasswordAuthenticator {
public Object authenticate(String username, String password) {
return (username != null && username.equals(password)) ?
username : null;
}
}
==================================================
<bean name="sshServer" class="com.google.code.sshd.SshServer"
init-method="start" destroy-method="stop">
<property name="port" value="8000" />
<property name="shellFactory">
<bean
class
="org.apache.servicemix.kernel.gshell.core.sshd.GShellShellFactory">
<property name="branding" ref="branding" />
<property name="completers">
<list>
<ref bean="commandsCompleter"/>
<ref bean="aliasNameCompleter"/>
</list>
</property>
<property name="executor" ref="commandLineExecutor" />
<property name="history">
<bean
class="org.apache.geronimo.gshell.wisdom.shell.HistoryImpl">
<constructor-arg ref="application"/>
</bean>
</property>
<property name="prompter">
<bean
class="org.apache.geronimo.gshell.wisdom.shell.ConsolePrompterImpl">
<constructor-arg ref="application"/>
</bean>
</property>
</bean>
</property>
<property name="hostKeyProvider">
<bean
class="com.google.code.sshd.hostkeys.FileHostKeyProvider">
<constructor-arg>
<list>
<value>${hostKey}</value>
</list>
</constructor-arg>
</bean>
</property>
<property name="passwordAuthenticator">
<!-- TODO: provide real authentication -->
<bean
class
=
"org
.apache.servicemix.kernel.gshell.core.sshd.BogusPasswordAuthenticator"
/>
</property>
<!-- Do not use public keys for now
<property name="publickeyAuthenticator">
<bean
class="com.google.code.sshd.BogusPublickeyAuthenticator" />
</property>
-->
<!-- Standard properties -->
<property name="channelFactories">
<list>
<bean
class="com.google.code.sshd.channel.ChannelSession$Factory" />
</list>
</property>
<property name="cipherFactories">
<list>
<bean class="com.google.code.sshd.cipher.AES128CBC
$Factory" />
<bean
class="com.google.code.sshd.cipher.TripleDESCBC$Factory" />
<bean class="com.google.code.sshd.cipher.BlowfishCBC
$Factory" />
<bean class="com.google.code.sshd.cipher.AES192CBC
$Factory" />
<bean class="com.google.code.sshd.cipher.AES256CBC
$Factory" />
</list>
</property>
<property name="compressionFactories">
<list>
<bean
class="com.google.code.sshd.compression.CompressionNone$Factory" />
</list>
</property>
<property name="keyExchangeFactories">
<list>
<bean class="com.google.code.sshd.kex.DHG1$Factory" />
</list>
</property>
<property name="macFactories">
<list>
<bean
class="com.google.code.sshd.mac.HMACMD5$Factory" />
<bean
class="com.google.code.sshd.mac.HMACSHA1$Factory" />
<bean
class="com.google.code.sshd.mac.HMACMD596$Factory" />
<bean
class="com.google.code.sshd.mac.HMACSHA196$Factory" />
</list>
</property>
<property name="randomFactory">
<bean class="com.google.code.sshd.random.JceRandom
$Factory" />
</property>
<property name="userAuthFactories">
<list>
<bean
class="com.google.code.sshd.auth.UserAuthPublicKey$Factory" />
<bean
class="com.google.code.sshd.auth.UserAuthPassword$Factory" />
</list>
</property>
<property name="signatureFactories">
<list>
<bean
class="com.google.code.sshd.signature.SignatureDSA$Factory" />
<bean
class="com.google.code.sshd.signature.SignatureRSA$Factory" />
</list>
</property>
</bean>
===================================================
package org.apache.servicemix.kernel.gshell.core.sshd;
import java.util.Map;
import java.util.List;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.Closeable;
import java.io.IOException;
import com.google.code.sshd.ShellFactory;
import com.google.code.sshd.shell.CrLfFilterInputStream;
import org.apache.geronimo.gshell.shell.ShellContext;
import org.apache.geronimo.gshell.io.IO;
import org.apache.geronimo.gshell.command.Variables;
import org.apache.geronimo.gshell.console.Console;
import org.apache.geronimo.gshell.console.JLineConsole;
import
org.apache.geronimo.gshell.console.completer.AggregateCompleter;
import org.apache.geronimo.gshell.notification.ExitNotification;
import org.apache.geronimo.gshell.notification.ErrorNotification;
import org.apache.geronimo.gshell.application.model.Branding;
import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
import org.apache.geronimo.gshell.ansi.AnsiRenderer;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import jline.History;
import jline.Completor;
public class GShellShellFactory implements ShellFactory {
private Logger logger = LoggerFactory.getLogger(getClass());
private Branding branding;
private Console.Prompter prompter;
private CommandLineExecutor executor;
private History history;
private List<Completor> completers;
public Branding getBranding() {
return branding;
}
public void setBranding(Branding branding) {
this.branding = branding;
}
public Console.Prompter getPrompter() {
return prompter;
}
public void setPrompter(Console.Prompter prompter) {
this.prompter = prompter;
}
public CommandLineExecutor getExecutor() {
return executor;
}
public void setExecutor(CommandLineExecutor executor) {
this.executor = executor;
}
public History getHistory() {
return history;
}
public void setHistory(History history) {
this.history = history;
}
public List<Completor> getCompleters() {
return completers;
}
public void setCompleters(List<Completor> completers) {
this.completers = completers;
}
public Shell createShell() {
return new ShellImpl();
}
public class ShellImpl implements ShellFactory.DirectShell,
org.apache.geronimo.gshell.shell.Shell {
private InputStream in;
private OutputStream out;
private OutputStream err;
private IO io;
private Variables variables;
private ShellContext context;
private boolean closed;
public ShellImpl() {
}
public void setInputStream(InputStream in) {
this.in = in;
}
public void setOutputStream(OutputStream out) {
this.out = out;
}
public void setErrorStream(OutputStream err) {
this.err = err;
}
public void start(Map<String,String> env) throws Exception {
this.io = new IO(new CrLfFilterInputStream(in, "IN: ",
logger),
new LfToCrLfFilterOutputStream(out,
"OUT:", logger),
new LfToCrLfFilterOutputStream(err,
"ERR:", logger),
false);
this.variables = new Variables((Map) env);
this.context = new ShellContext() {
public org.apache.geronimo.gshell.shell.Shell
getShell() {
return ShellImpl.this;
}
public IO getIo() {
return ShellImpl.this.io;
}
public Variables getVariables() {
return ShellImpl.this.variables;
}
};
new Thread() {
public void run() {
try {
ShellImpl.this.run();
} catch (Exception e) {
e.printStackTrace();
} finally {
close();
}
}
}.start();
}
public boolean isAlive() {
return !closed;
}
public int exitValue() {
if (!closed) {
throw new IllegalThreadStateException();
}
return 0;
}
public void destroy() {
close();
}
public ShellContext getContext() {
return context;
}
public Object execute(String line) throws Exception {
return executor.execute(getContext(), line);
}
public Object execute(String command, Object[] args) throws
Exception {
return executor.execute(getContext(), args);
}
public Object execute(Object... args) throws Exception {
return executor.execute(getContext(), args);
}
public boolean isOpened() {
return !closed;
}
public void close() {
closed = true;
close(in);
close(out);
close(err);
}
public boolean isInteractive() {
return false;
}
public void run(Object... args) throws Exception {
Console.Executor executor = new Console.Executor() {
public Result execute(final String line) throws
Exception {
assert line != null;
try {
ShellImpl.this.execute(line);
}
catch (ExitNotification n) {
return Result.STOP;
}
return Result.CONTINUE;
}
};
IO io = getContext().getIo();
// Setup the console runner
JLineConsole console = new JLineConsole(executor, io);
console.setPrompter(getPrompter());
console.setErrorHandler(new ConsoleErrorHandlerImpl(io));
console.setHistory(getHistory());
if (completers != null) {
// Have to use aggregate here to get the completion
list to update properly
console.addCompleter(new
AggregateCompleter(completers));
}
console.run();
}
private void close(Closeable c) {
try {
c.close();
} catch (IOException e) {
// Ignore
}
}
}
public static class ConsoleErrorHandlerImpl implements
Console.ErrorHandler {
private final Logger log = LoggerFactory.getLogger(getClass());
private final IO io;
private AnsiRenderer renderer = new AnsiRenderer();
public ConsoleErrorHandlerImpl(final IO io) {
assert io != null;
this.io = io;
}
public Result handleError(final Throwable error) {
assert error != null;
displayError(error);
return Result.CONTINUE;
}
private void displayError(final Throwable error) {
assert error != null;
// Decode any error notifications
Throwable cause = error;
if (error instanceof ErrorNotification) {
cause = error.getCause();
}
//
// TODO: Use the Render API
//
// Spit out the terse reason why we've failed
io.err.print("@|bold,red ERROR| ");
io.err.print(cause.getClass().getSimpleName());
io.err.println(": @|bold,red " + cause.getMessage() + "|");
// Determine if the stack trace flag is set
String stackTraceProperty =
System.getProperty("gshell.show.stacktrace");
boolean stackTraceFlag = false;
if (stackTraceProperty != null) {
stackTraceFlag =
stackTraceProperty.trim().equals("true");
}
if (io.isDebug()) {
// If we have debug enabled then skip the fancy bits
below, and log the full error, don't decode shit
log.debug(error.toString(), error);
}
else if (io.isVerbose() || stackTraceFlag) {
// Render a fancy ansi colored stack trace
StackTraceElement[] trace = cause.getStackTrace();
StringBuilder buff = new StringBuilder();
//
// TODO: Move this to helper in gshell-ansi
//
for (StackTraceElement e : trace) {
buff.append(" @|bold at| ").
append(e.getClassName()).
append(".").
append(e.getMethodName()).
append(" (@|bold ");
buff.append(e.isNativeMethod() ? "Native Method" :
(e.getFileName() != null &&
e.getLineNumber() != -1 ? e.getFileName() + ":" + e.getLineNumber() :
(e.getFileName() != null ?
e.getFileName() : "Unknown Source")));
buff.append("|)");
//
// FIXME: This does not properly display the full
exception detail when cause contains nested exceptions
//
io.err.println(buff);
buff.setLength(0);
}
}
io.err.flush();
}
}
}
On Tue, Nov 11, 2008 at 8:07 AM, Jason Dillon
<[EMAIL PROTECTED]> wrote:
How does one hook up GShell to use this stuff?
--jason
On Nov 7, 2008, at 4:22 AM, Guillaume Nodet wrote:
Over the past days, I've been working on a implementing a SSH server
in java to replace to gshell remoting bits.
The project is currently hosted at google code:
http://code.google.com/p/sshd/
This project is based on Mina and the current status is that the ssh
protocol is in a working state, but there are still a lots of things
to iron.
I've been able to connect using openssh 5.0 and 5.1 and also jsch
(from which i borrowed from code btw) and launch an /bin/sh shell
and
issue a few commands.
I'd be happy if any committer is interested to work on that to give
him commits rights on the project.
--
Cheers,
Guillaume Nodet
------------------------
Blog: http://gnodet.blogspot.com/
------------------------
Open Source SOA
http://fusesource.com
--
Cheers,
Guillaume Nodet
------------------------
Blog: http://gnodet.blogspot.com/
------------------------
Open Source SOA
http://fusesource.com