dongzhuo5425
dongzhuo5425
2015-03-07 23:41

如何使用Go通过SSH发送终端转义序列?

已采纳

I'm writing a Go program that will connect to a host via SSH using the native x/crypto/ssh library and drop an interative shell.

I'm using RequestPty(), but the (bash) shell on the remote end does not behave as expected with control codes.

When I enter various control characters, they're echoed back by my terminal:

$ ^[[A

The characters still work in the sense that if I press enter after pressing the up arrow, the previous command is run - but the control character output clobbers what should be displayed there. The same goes for tab.

Is there some straightforward way to get this to work? When I've implemented similar systems in the past it hasn't been an issue because I've just shelled out to openssh, and the semantics of process groups sort it all out.

I've studied "The TTY Demystified" and as great as it is it's not clear where to begin.

A couple of things I've thought to investigate:

openssh itself must be doing this work correctly, but it's a real best of a code base to study.

It's not actually clear to me whether this printing is being done by my local terminal emulator or shell or by the code on the remote host.

Where do I begin?


Here is a sample of my 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()

I've tried this with term values other than xterm: screen-256color and vt100.

For the record - in the real code, instead of just a call to session.Wait(), I have a for/select loop that catches various signals to the process and sends them on to the Session.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • dongxiong4571 dongxiong4571 6年前

    Disable ECHOCTL terminal mode.

    modes := ssh.TerminalModes{
        ssh.ECHO: 0,
        ssh.ECHOCTL: 0,
        ssh.TTY_OP_ISPEED: 14400,
        ssh.TTY_OP_OSPEED: 14400
    }
    
    点赞 评论 复制链接分享
  • duanlu1922 duanlu1922 5年前

    The ssh.ECHO: 0 and ssh.ECHOCTL: 0 settings didn't work for me. For other people that ran into this issue, below is the rough code it took to get a fully working interactive terminal with the Go ssh library:

    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()
    

    Note that all keyboard functionality (tab completion, up arrow) and signal handling (CTRL + C, CTRL + D) works correctly with the setup above.

    点赞 评论 复制链接分享