如何使用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.

doukaojie8573
doukaojie8573 由于某种原因,session.Signal()从来没有为我工作。但是,请参阅下面我的新回复,以获取完整的解决方案。
4 年多之前 回复
duang8642
duang8642 我需要扩展实现以处理RFC中指定的每个信号,但目前我仅处理SIGINT和SIGQUIT。当我得到它们时,我将它们传递给session.Signal()。
4 年多之前 回复
drsqpko5286
drsqpko5286 您在等待/选择什么信号?拿到他们后,您对他们做了什么?我似乎无法使SIGINT(CTRL+C)以我期望的方式工作。
4 年多之前 回复
dongtao9158
dongtao9158 我将我的评论发布为答案,因此您可以将此答案标记为已回答。
5 年多之前 回复
dongzhina7098
dongzhina7098 请记住回答您自己的问题并接受答案:)
5 年多之前 回复
doudai8083
doudai8083 修好了!现在了解原因。
5 年多之前 回复
douxian4888
douxian4888 您是否尝试过禁用ECHOCTL终端模式?
5 年多之前 回复

2个回答



禁用 ECHOCTL </ code>终端模式。</ p>

  modes:  = ssh.TerminalModes {
ssh.ECHO:0,
ssh.ECHOCTL:0,
ssh.TTY_OP_ISPEED:14400,
ssh.TTY_OP_OSPEED:14400
}
</ code> </ pre> \ n </ div>

展开原文

原文

Disable ECHOCTL terminal mode.

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

drtkyykai004574380
drtkyykai004574380 我已经在代码中将ssh.ECHOCTL设置为0,并且在使用箭头键时仍然看到控制字符。 有任何想法吗?
4 年多之前 回复
douwen0612
douwen0612 这对我没用。 我仍然看到字符转义码,并且自动完成功能不起作用。
4 年多之前 回复

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.

doupin5667
doupin5667 因此,基本上,问题在于os.Stdin是否以某种方式被缓冲? 谢谢你的帮助,我真的很沮丧,试图找出解决这个问题的方法。
3 年多之前 回复
duanbei1598
duanbei1598 你救了我的日子
大约 4 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐