termmode_windows.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // +build windows
  2. // Note the terminal manipulation functions herein are mostly stubs. They
  3. // don't really do anything and the xs demo client depends on a wrapper
  4. // script using the 'stty' tool to actually set the proper mode for
  5. // password login and raw mode required, then restoring it upon logout/exit.
  6. //
  7. // mintty uses named pipes and ptys rather than Windows 'console'
  8. // mode, and Go's x/crypto/ssh/terminal libs only work for the latter, so
  9. // until some truly cross-platform terminal mode handling makes it into the
  10. // go std lib I'm not going to jump through hoops trying to be cross-platform
  11. // here; the wrapper does the bare minimum to make the client workable
  12. // under MSYS+mintty which is what I use.
  13. package xs
  14. import (
  15. "io"
  16. "os/exec"
  17. "golang.org/x/sys/windows"
  18. )
  19. type State struct {
  20. }
  21. // MakeRaw put the terminal connected to the given file descriptor into raw
  22. // mode and returns the previous state of the terminal so that it can be
  23. // restored.
  24. func MakeRaw(fd uintptr) (*State, error) {
  25. // This doesn't really work. The exec.Command() runs a sub-shell
  26. // so the stty mods don't affect the client process.
  27. cmd := exec.Command("stty", "-echo raw")
  28. cmd.Run()
  29. return &State{}, nil
  30. }
  31. // GetState returns the current state of a terminal which may be useful to
  32. // restore the terminal after a signal.
  33. func GetState(fd uintptr) (*State, error) {
  34. return &State{}, nil
  35. }
  36. // Restore restores the terminal connected to the given file descriptor to a
  37. // previous state.
  38. func Restore(fd uintptr, state *State) error {
  39. cmd := exec.Command("stty", "echo cooked")
  40. cmd.Run()
  41. return nil
  42. }
  43. // ReadPassword reads a line of input from a terminal without local echo. This
  44. // is commonly used for inputting passwords and other sensitive data. The slice
  45. // returned does not include the \n.
  46. func ReadPassword(fd uintptr) ([]byte, error) {
  47. return readPasswordLine(passwordReader(fd))
  48. }
  49. // passwordReader is an io.Reader that reads from a specific file descriptor.
  50. type passwordReader windows.Handle
  51. func (r passwordReader) Read(buf []byte) (int, error) {
  52. return windows.Read(windows.Handle(r), buf)
  53. }
  54. // readPasswordLine reads from reader until it finds \n or io.EOF.
  55. // The slice returned does not include the \n.
  56. // readPasswordLine also ignores any \r it finds.
  57. func readPasswordLine(reader io.Reader) ([]byte, error) {
  58. var buf [1]byte
  59. var ret []byte
  60. for {
  61. n, err := reader.Read(buf[:])
  62. if n > 0 {
  63. switch buf[0] {
  64. case '\n':
  65. return ret, nil
  66. case '\r':
  67. // remove \r from passwords on Windows
  68. default:
  69. ret = append(ret, buf[0])
  70. }
  71. continue
  72. }
  73. if err != nil {
  74. if err == io.EOF && len(ret) > 0 {
  75. return ret, nil
  76. }
  77. return ret, err
  78. }
  79. }
  80. }