Ganymed-ssh2-build210

6 respostas
Z

Olá!

Gostaria de poder contar com a ajuda de alguém que tenha bastante paciência com um newbie dedicado.

É o seguinte: caí de pára-quedas numa empresa que está desenvolvendo um sistema (em Java) para filtrar logs de IPs gerados por um servidor (Linux) de acesso. Atualmente o processo é efetuado através de um shell script rodando localmente na máquina onde os logs são gravados. Antes da execução do script, um usuário, através do Putty, efetua uma conexão a um servidor (gateway), e de lá abre uma nova conexão SSH para a máquina onde o script rodará, e após sua execução, os arquivos filtrados são copiados, através do WinSCP, para a máquina do usuário.

O propósito da aplicação que estou fazendo é permitir que usuários, a partir de máquinas Windows, possam enviar arquivos (arquivos com dados de entrada que são lidos pelo shell script) para o servidor Linux diretamente , executar o script, e copiar o resultado gerado para a máquina do usuário.

Para realizar a conexão e execução do script, estou usando a biblioteca ganymed-ssh2-build210, porém encontro algumas dificuldades, que vou citar abaixo:

Não consigo interagir com o terminal virtual que é criado ao ser aberta a conexão via SSH. Consigo somente enviar comandos que não exigem interação com o teclado para que o comando possa ser concluído. Exemplo: consigo enviar um “ls” e visualizar o resultado, mas não consigo rodar um “ssh usuario@servidor” pois é necessária a digitação da senha após o envio do comando. Esta necessidade de efetuar uma nova conexão SSH, após ser aberta a primeira conexão através da biblioteca, se justifica pelo fato de a máquina do usuário não poder efetuar conexões SSH diretamente para máquinas fora de sua rede. Então o que ele faz é conectar-se, via Putty, para uma máquina (gateway) e, a partir dela, conectar-se novamente, via SSH, para a outra máquina onde o script será executado. Antes que me sugiram usar autenticação através de chaves públicas e privadas, informo que há restrições da área de segurança quanto a isso. A única alternativa que tenho é interagir com o teclado. Esta máquina que serve como ponte não permite escrita aos usuários que a utilizam, inclusive…

Li bastante a documentação da biblioteca, e em alguns outros fóruns, vi sugestões de como implementar esta biblioteca, usando interação com o teclado para enviar comandos que exigem essa interação, porém entendi bulhufas. Ressalto novamente que sou newbie, e caí de pára-quedas no universo Java.

Alguém poderia, por gentileza, me fornecer um exemplo de implementação desta biblioteca, que permita o envio de comandos, resgate de mensagens do servidor, e que permita interação com o mesmo?

Muito obrigado.

[]'s

6 Respostas

Gustavokt

Você tentou o Basic.java? Não é exatamente o que você precisa? Está na pasta examples do zip dessa lib…

Z

Já tentei, sim. O exemplo desta classe é justamente o que não tem interação com o teclado.

Encontrei a seguinte implementação em outro forum:

package multiploscomandosssh;

import ch.ethz.ssh2.ChannelCondition;

import ch.ethz.ssh2.Connection;

import ch.ethz.ssh2.KnownHosts;

import ch.ethz.ssh2.Session;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.OutputStreamWriter;

public class TestClient01 implements Runnable {

static final String knownHostPath = "~/.ssh/known_hosts";
static final String idDSAPath = "~/.ssh/id_dsa";
static final String idRSAPath = "~/.ssh/id_rsa";
private String host = "localhost";
private String username = "dial";
private String password = "123456";
private KnownHosts database = new KnownHosts();
private OutputStreamWriter writer = null;
private Connection conn = null;
private Session sess = null;

public void writeCommand(String s) {
    try {
        writer.write(s);
        writer.flush();
    } catch(Exception ex) {}
}

public void close() {
    if (writer==null) return;
    try {
        writer.close();
        sess.close();
        conn.close();
    } catch(Exception ex) {}
    if (writer!=null) writer = null;
}

public void run() {
    try {
        conn = new Connection(host);
        conn.connect();

        boolean isAuthenticated = conn.authenticateWithPassword(username, password);

        if (!isAuthenticated) throw new IOException("Authentication failed.");

        sess = conn.openSession();

        new Thread(new SyncPipe(sess.getStderr(), System.err)).start();
        new Thread(new SyncPipe(sess.getStdout(), System.out)).start();
        
        sess.requestPTY("bash");
        sess.startShell();

        writer = new OutputStreamWriter(sess.getStdin(), "utf-8");
        writeCommand("pwd \n");
        writeCommand("ssh renato@localhost \n");
       
       
        sess.waitForCondition(ChannelCondition.CLOSED | ChannelCondition.EOF |
                ChannelCondition.EXIT_STATUS, 5000);
           

        System.out.println("Exit status : " + sess.getExitStatus());
    }
   catch (Exception e) {
        e.printStackTrace(System.err);
        System.exit(2);
    }
    finally{

        sess.close();
        conn.close();

    }

}

class SyncPipe implements Runnable {
    String CVS_VERSION = "$Revision: 1.1 $ $Id: SyncPipe.java,v 1.1 2008-09-30 03:47:56 sabre Exp $ ";
    private final byte[] buffer_;
    private final OutputStream ostrm_;
    private final InputStream istrm_;
    private boolean closeAfterCopy_ = false;

    public SyncPipe(InputStream istrm, OutputStream ostrm) {
        this(istrm, ostrm, 4096);
    }

    public SyncPipe(InputStream istrm, OutputStream ostrm, int bufferSize) {

        if (istrm == null) throw new IllegalArgumentException("'istrm' cannot be null");
        if (ostrm == null) throw new IllegalArgumentException("'ostrm' cannot be null");
        if (bufferSize < 1024) throw new IllegalArgumentException("a buffer size less than 1024 makes little sense");
        istrm_ = istrm;
        ostrm_ = ostrm;
        buffer_ = new byte[bufferSize];
    }
    public void handleException(IOException e) {
        e.printStackTrace();
    }

    public SyncPipe setCloseAfterCopy(boolean closeAfterCopy) {
        closeAfterCopy_ = closeAfterCopy;
        return this;
    }
    public void run() {
        try {
            for (int bytesRead = 0; (bytesRead = istrm_.read(buffer_)) != -1;)
                ostrm_.write(buffer_, 0, bytesRead);
            ostrm_.flush();
            if (closeAfterCopy_) ostrm_.close();
        } catch (IOException e) {
            handleException(e);
        }
    }
}

 public static void main(String[] args) throws Exception {
    new TestClient01().run();
}

}

Com esta implementação, consigo ver o pedido de senha que o terminal devolve, porém não sei como responder a este pedido.

Gustavokt

Hum, pelo que eu tinha visto, talvez até dava pra você aproveitar o Basic mesmo.

/* Create a session */

Session sess = conn.openSession();

sess.execCommand("uname -a && date && uptime && who");

System.out.println("Here is some information about the remote host:");

Esse outro exemplo que você passou vai ser um mais complexo de entender mesmo (está mexendo com thread), embora dependendo do que você precise fazer, a 2a. solução possa ser a que você precise usar…

Um teste que você poderia fazer é tentar pegar o Basic mesmo e ver nesse trecho que eu destaquei.

Lá foi aberto uma sessão com o SSH e feito a execução de um comando “uname -a && date && uptime && who”.

Se não estou enganado (faz um tempinho que não mexo no linux), esse é um comando de linux.

A saída para esse comando pode ser visto na parte de baixo desse trecho

/* 
 * This basic example does not handle stderr, which is sometimes dangerous
 * (please read the FAQ).
 */

InputStream stdout = new StreamGobbler(sess.getStdout());

BufferedReader br = new BufferedReader(new InputStreamReader(stdout));

while (true)
{
	String line = br.readLine();
	if (line == null)
		break;
	System.out.println(line);
}

Para você poder interagir com isso, você precisaria fazer um loop com esses dois trechos e pegar os comando que você digitar no shell

Para pegar os inputs do shell veja
http://www.guj.com.br/posts/list/46970.java

Espero não ter dado informação errada para você (não estou usando a lib, mas acho que deve funcionar da maneira que te disse)

No caso do Basic não ser suficiente para fazer tudo o que você precisa, podemos dar uma olhada no outro código que você mandou.
Se estiver difícil, só continuar postando

Z

A saída do comando eu já consigo recuperar. Esta implementação de exemplo (Basic.java) apenas envia comandos para a máquina Linux e imprime os resultados na tela, não permite o envio de respostas.

Na documentação da biblioteca sugere-se usar o método requestPTY() do objeto Session, para poder abrir um terminal virtual e interagir com o mesmo, e é justamente isso o que a outra implementação faz.

Minha dificuldade continua em não saber como enviar as respostas aos comandos que exigem interação com o teclado.

De qualquer forma, muito obrigado pela sua resposta,Gustavokt. Estou olhando o link que você enviou.

Gustavokt

zerokarl:
A saída do comando eu já consigo recuperar. Esta implementação de exemplo (Basic.java) apenas envia comandos para a máquina Linux e imprime os resultados na tela, não permite o envio de respostas.

Na verdade, a idéia era alterar o Basic.java…

Bom, se você realmente precisa de um terminal virtual, acho que não tem como fugir do código que vc postou

A minha ideia era algo do tipo:

//abre sessão
Session sess = conn.openSession();

while(naoSessaoEhValida...) { // aqui você teria que definir caso fosse na linha de comando...
   String pegaComando = pegaComandos(); //link que eu passei diz como pegar comandos do teclado...
   sess.execCommand(pegaComando);

   //mostra na tela resposta do servidor
   mostraResposta(); //o segundo trecho...
}

Mas se eu estiver enganado, me desculpe…

BILL

Olá pessoal!
estou usando ssh2 também, e estou com uma dúvida, se alguém puder ajudar.

estou tentando deixar uma sessão aberta, para enviar vários comandos.
a conexão já fica aberta, mas estou com problemas não enviar um segundo comando para um servidor!

o erro é esse:
java.io.IOException: A remote execution has already started.

depois que é executado o primeiro comando, e retornado isso ai!

eu fiz uma modificação na classe de exemplo PublicKeyAuthentication, que vem junto no download do ganymed!

eu quero que minha aplicação finalize a sessão e a conexão!

grato!

Criado 17 de agosto de 2010
Ultima resposta 20 de mai. de 2011
Respostas 6
Participantes 3