// Manhole connects os.Stdin, os.Stdout, and os.Stderr to an interactive shell // session on the Machine m. Manhole blocks until the shell session has ended. // If os.Stdin does not refer to a TTY, Manhole returns immediately with a nil // error. func Manhole(m Machine) error { fd := int(os.Stdin.Fd()) if !terminal.IsTerminal(fd) { return nil } tstate, _ := terminal.MakeRaw(fd) defer terminal.Restore(fd, tstate) client, err := m.SSHClient() if err != nil { return fmt.Errorf("SSH client failed: %v", err) } defer client.Close() session, err := client.NewSession() if err != nil { return fmt.Errorf("SSH session failed: %v", err) } defer session.Close() session.Stdin = os.Stdin session.Stdout = os.Stdout session.Stderr = os.Stderr modes := ssh.TerminalModes{ ssh.TTY_OP_ISPEED: 115200, ssh.TTY_OP_OSPEED: 115200, } cols, lines, err := terminal.GetSize(int(os.Stdin.Fd())) if err != nil { return err } if err = session.RequestPty(os.Getenv("TERM"), lines, cols, modes); err != nil { return fmt.Errorf("failed to request pseudo terminal: %s", err) } if err := session.Shell(); err != nil { return fmt.Errorf("failed to start shell: %s", err) } if err := session.Wait(); err != nil { return fmt.Errorf("failed to wait for session: %s", err) } return nil }
// isTerminal returns True when w is going to a tty, and false otherwise. func isTerminal(w io.Writer) bool { if f, ok := w.(*os.File); ok { return terminal.IsTerminal(int(f.Fd())) } return false }