Как отправить escape-последовательности терминала через SSH с Go?
Я пишу программу Go, которая будет подключаться к хосту через SSH с помощью native x / crypto / ssh библиотека и отбросьте интерактивную оболочку.
Я использую RequestPty()
, но оболочка (bash) на удаленном конце ведет себя не так, как ожидалось с управляющими кодами.
когда я ввожу различные управляющие символы, они отзываются эхом на моем терминале:
$ ^[[A
символы все еще работа в том смысле, что если я нажмите enter после нажатия стрелки вверх выполняется предыдущая команда - но управляющий символ выводит то, что должно отображаться там. То же самое касается вкладки.
есть ли какой-то простой способ заставить это работать? Когда я реализовывал подобные системы в прошлом, это не было проблемой, потому что я только что выкрикнул openssh
, и семантика групп процессов сортирует все это.
Я изучал "телетайпом Демистифицированный" и как большой а так непонятно, с чего начать.
пара вещей, которые я думал, что расследовать:
- включить режим raw в моем терминале - но я не хочу делать этого, не понимая почему, и это не кажется поступить правильно
- возиться с флагами терминального режима
openssh
сам должен делать эту работу правильно, но это реальное лучшее из базы кода для изучать.
на самом деле мне не ясно, выполняется ли эта печать моим эмулятором локального терминала или оболочкой или кодом на удаленном хосте.
С чего начать?
вот пример моего кода:
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()
Я пробовал это со значениями терминов, отличными от xterm
: screen-256color
и vt100
.
Для справки - в реальном коде, а не просто вызов session.Wait()
, у меня есть for/select
петля, которая ловит различные сигналы к процессу и отправляет их на Session
.
2 ответов
отключить ECHOCTL
режим терминала.
modes := ssh.TerminalModes{
ssh.ECHO: 0,
ssh.ECHOCTL: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400
}
на ssh.ECHO: 0
и ssh.ECHOCTL: 0
настройки не работали для меня. Для других людей, которые столкнулись с этой проблемой, ниже приведен примерный код, необходимый для получения полностью работающего интерактивного терминала с библиотекой GO ssh:
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()
обратите внимание, что все функции клавиатуры (завершение вкладки, стрелка вверх) и обработка сигналов (CTRL + C
, CTRL + D
) работает правильно с настройкой выше.