I have this "sort of" working, but here is what is missing:
- control keys do not work. "ctrl-c" kills the entire go script, "ctrl-r" does not work as expected, etc.
- up and down arrows print
^[[A^[[D^[[C
The code I have below I got from this package: https://github.com/nanobox-io/golang-ssh I tested using that package separately and it works perfectly. I can't see what that package is doing that isn't in my script. I've also included the code I use to create a tunnel to the host, in case that is relevant.
Here is the example code. Please disregard the use of panic, it makes it easier for me to debug this new code while I am working on it. I would not intend this code in its current state to be used in a production environment.
func Start(hostName string) {
var (
termWidth, termHeight = 80, 24
)
jumpAddr := "..."
hostAddr := "..."
jumpConfig := ssh.ClientConfig{
// ...
}
hostConfig := ssh.ClientConfig{
// ...
}
jumpConn, err := ssh.Dial("tcp", jumpAddr+":22", &jumpConfig)
ifpanic(err)
hostConn, err := jumpConn.Dial("tcp", hostAddr+":22")
ifpanic(err)
hostSSHConn, chans, reqs, err := ssh.NewClientConn(hostConn, hostName+":22", &hostConfig)
ifpanic(err)
hostClient := ssh.NewClient(hostSSHConn, chans, reqs)
session, err := hostClient.NewSession()
ifpanic(err)
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
modes := ssh.TerminalModes{
ssh.ECHO: 1,
}
fd := os.Stdin.Fd()
oldState, err := term.MakeRaw(fd)
ifpanic(err)
defer ifpanic(term.RestoreTerminal(fd, oldState))
winsize, err := term.GetWinsize(fd)
if err == nil {
termWidth = int(winsize.Width)
termHeight = int(winsize.Height)
}
if err := session.RequestPty("xterm", termHeight, termWidth, modes); err != nil {
panic(err)
}
if err := session.Shell(); err != nil {
panic(err)
}
go monWinCh(session, os.Stdout.Fd())
if err := session.Wait(); err != nil {
panic(err)
}
}
func ifpanic(err error) {
if err != nil {
panic(err)
}
}
func monWinCh(session *ssh.Session, fd uintptr) {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGWINCH)
defer signal.Stop(sigs)
for range sigs {
if _, err := session.SendRequest("window-change", false, termSize(fd)); err != nil {
panic(err)
}
}
}
func termSize(fd uintptr) []byte {
size := make([]byte, 16)
winsize, err := term.GetWinsize(fd)
if err != nil {
binary.BigEndian.PutUint32(size, uint32(80))
binary.BigEndian.PutUint32(size[4:], uint32(24))
return size
}
binary.BigEndian.PutUint32(size, uint32(winsize.Width))
binary.BigEndian.PutUint32(size[4:], uint32(winsize.Height))
return size
}