github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/net/terminal/client/impl.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "golang.org/x/crypto/ssh/terminal" 9 ) 10 11 type flushWriter interface { 12 Flush() error 13 io.Writer 14 } 15 16 func startTerminal(conn FlushReadWriter) error { 17 oldState, err := terminal.MakeRaw(int(os.Stdin.Fd())) 18 if err != nil { 19 return err 20 } 21 defer terminal.Restore(int(os.Stdin.Fd()), oldState) 22 retval := make(chan error, 2) 23 go func(retval chan<- error) { 24 retval <- readFromConnection(conn) 25 }(retval) 26 go func(retval chan<- error) { 27 retval <- readFromStdin(conn) 28 }(retval) 29 return <-retval 30 } 31 32 func readFromConnection(conn io.Reader) error { 33 // Read from connection until EOF, write to stdout. 34 buffer := make([]byte, 256) 35 for { 36 if nRead, err := conn.Read(buffer); err != nil { 37 if err == io.EOF { 38 return nil 39 } 40 return fmt.Errorf("error reading from remote shell: %s\n", err) 41 } else { 42 os.Stderr.Write(buffer[:nRead]) 43 } 44 } 45 } 46 47 func readFromStdin(conn flushWriter) error { 48 // Read from stdin until control-\ (File separator), write to connection. 49 for { 50 buffer := make([]byte, 256) 51 if nRead, err := os.Stdin.Read(buffer); err != nil { 52 return fmt.Errorf("error reading from stdin: %s", err) 53 } else { 54 if buffer[0] == '\x1c' { // Control-\: File separator. 55 return nil 56 } 57 if _, err := conn.Write(buffer[:nRead]); err != nil { 58 return fmt.Errorf("error writing to connection: %s", err) 59 } 60 if err := conn.Flush(); err != nil { 61 return fmt.Errorf("error flushing connection: %s", err) 62 } 63 } 64 } 65 }