【问题标题】:How to handle EOF / Ctrl+D in Go crypto/ssh session.Wait()如何在 Go crypto/ssh session.Wait() 中处理 EOF / Ctrl+D
【发布时间】:2019-07-30 11:41:53
【问题描述】:

当我使用session.Shell() 在远程服务器上启动一个新的 shell,然后使用session.Wait() 运行会话直到远程端退出会话时,使用 Ctrl+D 结束会话时无法正常处理。

我可以使用 os/exec 启动本地子进程来运行本地可用的任何 ssh 客户端副本,但我更喜欢使用本机 Go 来完成这项工作。

示例代码sn-p:

conn, err := ssh.Dial("tcp", "some-server.fqdn", sshConfig)
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,
}

err = session.RequestPty("xterm", 80, 40, modes)
if err != nil {
  return err
}

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

在远程服务器上运行exit 优雅地挂起远程端,session.Wait() 按预期返回,但发送带有 Ctrl+D 的 EOF 会导致远程端挂起,但对 session.Wait() 的调用被阻塞.我必须使用 Ctrl+C 对 Go 程序进行 SIGINT。 我想让两者都优雅地退出 session.Wait() 调用,因为这是大多数交互式 ssh 会话的预期行为。

【问题讨论】:

    标签: go ssh


    【解决方案1】:

    我能够重现这一点(使用一堆额外的框架代码),但我不确定它为什么会发生。如果您在标准输入上遇到 EOF,可以通过添加 session.Close 调用来终止会话:

            session.Stdout = os.Stdout
            session.Stderr = os.Stderr
            // session.Stdin = os.Stdin
            ip, err := session.StdinPipe()
            if err != nil {
                    return err
            }
            go func() {
                    io.Copy(ip, os.Stdin)
                    fmt.Println("stdin ended")
                    time.Sleep(1 * time.Second)
                    fmt.Println("issuing ip.Close() now")
                    ip.Close()
                    time.Sleep(1 * time.Second)
                    fmt.Println("issuing session.Close() now")
                    err = session.Close()
                    if err != nil {
                            fmt.Printf("close: %v\n", err)
                    }
            }()
    

    您会看到会话在session.Close() 之后关闭(不是很好)。调用ip.Close() 应该 已经关闭了标准输入通道,而且似乎在直接使用os.Stdin 时也会发生这种情况应该,但由于某种原因它不起作用.调试显示一条 ssh-channel-close 消息发送到另一端(两种情况下),但另一端没有关闭返回数据 ssh 通道,因此您的端继续等待它们的更多输出。

    值得注意的是:您没有将本地 tty 置于 raw-ish character-at-a-time 模式。常规的 ssh 会话会,因此 ^D 实际上并没有关闭 连接,它只是将文字 control-D 发送到另一端的 pty。另一端将 control-D 转换为 pty 上的(软)EOF 信号。

    【讨论】:

    • 看来goroutine应该在session.Wait()内部。该文档建议无论连接如何关闭它都应该返回,只是在非优雅终止下将填充 err 值。您知道使用 ssh 包进入原始模式的方法吗?我有兴趣尝试一下。将 EOF 发送到远程端应该会导致 bash 很好地退出。
    • 不确定你指的是哪个 goroutine——已经有一个与上面的类似(但缺少 session.Close() 部分)处理从输入流到会话另一端的复制。至于获取raw模式,不知道有没有什么方便的。
    猜你喜欢
    • 1970-01-01
    • 2017-05-04
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 2012-02-07
    • 2018-03-21
    • 1970-01-01
    相关资源
    最近更新 更多