2015-03-07 2 views
8

Ich schreibe ein Go-Programm, das über die native x/crypto/ssh-Bibliothek eine Verbindung zu einem Host über SSH herstellt und eine interaktive Shell ablegt.Wie kann ich terminale Escape-Sequenzen über SSH mit Go senden?

Ich verwende RequestPty(), aber die (Bash) Shell auf dem Remote-Ende verhält sich nicht wie erwartet mit Steuercodes.

Wenn ich verschiedene Steuerzeichen eingeben, werden sie wieder von meinem Terminal hallte:

$ ^[[A 

Die Zeichen, die noch Arbeit im Sinn, wenn ich drücken Sie nach dem Pfeil nach oben drücken, der vorherige Befehl wird ausgeführt - aber die Steuerzeichenausgabe übertrumpft was dort angezeigt werden soll. Das gleiche gilt für Tab.

Gibt es einen einfachen Weg, dies zum Laufen zu bringen? Wenn ich in der Vergangenheit ähnliche Systeme implementiert habe, war das kein Problem, denn ich habe gerade auf openssh geschossen, und die Semantik der Prozessgruppen sortiert alles aus.

Ich habe studiert "The TTY Demystified" und so gut wie es ist, ist es nicht klar, wo ich anfangen soll.

Ein paar Dinge, die ich gedacht habe, zu untersuchen:

openssh selbst muss diese Arbeit richtig tun, aber es ist ein echter beste aus einer Code-Basis zu untersuchen.

Es ist mir nicht wirklich klar, ob dieser Druck von meinem lokalen Terminal-Emulator oder Shell oder vom Code auf dem Remote-Host ausgeführt wird.

Wo fange ich an?


Hier ist ein Beispiel von meinem Code:

conf := ssh.ClientConfig{ 
    User: myuser, 
    Auth: []ssh.AuthMethod{ssh.Password(my_password)} 
} 
conn, err := ssh.Dial("tcp", myhost, conf) 
if err != nil { 
    return err 
} 
defer conn.Close() 
session, err := conn.NewSession() 
if err != nil { 
    return err 
} 
defer session.Close() 
session.Stdout = os.Stdout 
session.Stderr = os.Stderr 
session.Stdin = os.Stdin 

modes := ssh.TerminalModes{ 
    ssh.ECHO: 0 
    ssh.TTY_OP_ISPEED: 14400, 
    ssh.TTY_OP_OSPEED: 14400 
} 
if err := session.RequestPty("xterm", 80, 40, modes); err != nil { 
    return err 
} 
if err = session.Shell(); err != nil { 
    return err 
} 

return session.Wait() 

ich dies mit Zeitwerte versucht haben, andere als xterm: screen-256color und vt100.

Für das Protokoll - in der realen Code, anstatt nur einen Aufruf session.Wait(), habe ich eine for/select Schleife, die verschiedene Signale an den Prozess fängt und sendet sie an den Session auf.

+3

Haben Sie versucht, den ECHOCTL-Terminalmodus zu deaktivieren? –

+0

@EmilDavtyan, die es behoben! Jetzt zu verstehen, warum. – Cera

+1

@Cerales Bitte denken Sie daran, Ihre eigene Frage zu beantworten und die Antwort zu akzeptieren :) – user918176

Antwort

1

Deaktivieren Sie ECHOCTL Terminal-Modus.

modes := ssh.TerminalModes{ 
    ssh.ECHO: 0, 
    ssh.ECHOCTL: 0, 
    ssh.TTY_OP_ISPEED: 14400, 
    ssh.TTY_OP_OSPEED: 14400 
} 
+2

Das funktioniert bei mir nicht. Ich sehe immer noch Zeichen Escapes und Autocomplete funktioniert nicht. – aramadia

+0

Ich habe 'ssh.ECHOCTL' in meinem Code auf 0 gesetzt und sehe immer noch Steuerzeichen, wenn ich die Pfeiltasten benutze. Irgendwelche Ideen? –

6

Die ssh.ECHO: 0 und ssh.ECHOCTL: 0 Einstellungen funktionierte nicht für mich.Für andere Menschen, die in diese Ausgabe liefen, unten ist der grobe Code es ein voll funktionsfähiges interaktives Terminal mit der Go ssh Bibliothek erhalten hat:

config := return &ssh.ClientConfig{ 
    User: "username", 
    Auth: []ssh.AuthMethod{ssh.Password("password")}, 
} 

client, err := ssh.Dial("tcp", "12.34.56.78:22", config) 
if err != nil { ... } 
defer client.Close() 

session, err := client.NewSession() 
if err != nil { ... } 
defer session.Close() 

session.Stdout = os.Stdout 
session.Stderr = os.Stderr 
session.Stdin = os.Stdin 

modes := ssh.TerminalModes{ 
    ssh.ECHO:   1,  // enable echoing 
    ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud 
    ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud 
} 

fileDescriptor := int(os.Stdin.Fd()) 

if terminal.IsTerminal(fileDescriptor) { 
    originalState, err := terminal.MakeRaw(fileDescriptor) 
    if err != nil { ... } 
    defer terminal.Restore(fileDescriptor, originalState) 

    termWidth, termHeight, err := terminal.GetSize(fileDescriptor) 
    if err != nil { ... } 

    err = session.RequestPty("xterm-256color", termHeight, termWidth, modes) 
    if err != nil { ... } 
} 

err = session.Shell() 
if err != nil { ... } 

// You should now be connected via SSH with a fully-interactive terminal 
// This call blocks until the user exits the session (e.g. via CTRL + D) 
session.Wait() 

Beachten Sie, dass alle Tastaturfunktionen (Tabulatorvervollständigung, Pfeil nach oben) und Signal Handhabung (CTRL + C, CTRL + D) funktioniert korrekt mit dem obigen Setup.

+0

Sie haben meinen Tag gerettet – ukautz

+0

Also ist das Problem, dass 'os.Stdin' irgendwie gepuffert wird? Danke dafür, ich war wirklich frustriert, herauszufinden, wie ich dieses Problem lösen könnte. –

Verwandte Themen