termmode_bsd.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // +build freebsd
  2. package xs
  3. import (
  4. "errors"
  5. "io"
  6. "unsafe"
  7. unix "golang.org/x/sys/unix"
  8. )
  9. /* -------------
  10. * minimal terminal APIs brought in from ssh/terminal
  11. * (they have no real business being there as they aren't specific to
  12. * ssh, but as of Go v1.10, late 2019, core go stdlib hasn't yet done
  13. * the planned terminal lib reorgs.)
  14. * ------------- */
  15. // From github.com/golang/crypto/blob/master/ssh/terminal/util_linux.go
  16. const getTermios = unix.TIOCGETA
  17. const setTermios = unix.TIOCSETA
  18. // From github.com/golang/crypto/blob/master/ssh/terminal/util.go
  19. // State contains the state of a terminal.
  20. type State struct {
  21. termios unix.Termios
  22. }
  23. // MakeRaw put the terminal connected to the given file descriptor into raw
  24. // mode and returns the previous state of the terminal so that it can be
  25. // restored.
  26. func MakeRaw(fd uintptr) (*State, error) {
  27. var oldState State
  28. if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
  29. return nil, err
  30. }
  31. newState := oldState.termios
  32. newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
  33. newState.Oflag &^= unix.OPOST
  34. newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
  35. newState.Cflag &^= (unix.CSIZE | unix.PARENB)
  36. newState.Cflag |= unix.CS8
  37. newState.Cc[unix.VMIN] = 1
  38. newState.Cc[unix.VTIME] = 0
  39. if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
  40. return nil, err
  41. }
  42. return &oldState, nil
  43. }
  44. // Restore restores the terminal connected to the given file descriptor to a
  45. // previous state.
  46. func Restore(fd uintptr, state *State) error {
  47. if state != nil {
  48. if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(state))); err != 0 {
  49. return err
  50. } else {
  51. return nil
  52. }
  53. } else {
  54. return errors.New("nil State")
  55. }
  56. }
  57. // ReadPassword reads a line of input from a terminal without local echo. This
  58. // is commonly used for inputting passwords and other sensitive data. The slice
  59. // returned does not include the \n.
  60. func ReadPassword(fd uintptr) ([]byte, error) {
  61. var oldState State
  62. if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
  63. return nil, err
  64. }
  65. newState := oldState.termios
  66. newState.Lflag &^= unix.ECHO
  67. newState.Lflag |= unix.ICANON | unix.ISIG
  68. newState.Iflag |= unix.ICRNL
  69. if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
  70. return nil, err
  71. }
  72. defer func() {
  73. unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&oldState.termios)))
  74. }()
  75. return readPasswordLine(passwordReader(fd))
  76. }
  77. // passwordReader is an io.Reader that reads from a specific file descriptor.
  78. type passwordReader int
  79. func (r passwordReader) Read(buf []byte) (int, error) {
  80. return unix.Read(int(r), buf)
  81. }
  82. // readPasswordLine reads from reader until it finds \n or io.EOF.
  83. // The slice returned does not include the \n.
  84. // readPasswordLine also ignores any \r it finds.
  85. func readPasswordLine(reader io.Reader) ([]byte, error) {
  86. var buf [1]byte
  87. var ret []byte
  88. for {
  89. n, err := reader.Read(buf[:])
  90. if n > 0 {
  91. switch buf[0] {
  92. case '\n':
  93. return ret, nil
  94. case '\r':
  95. // remove \r from passwords on Windows
  96. default:
  97. ret = append(ret, buf[0])
  98. }
  99. continue
  100. }
  101. if err != nil {
  102. if err == io.EOF && len(ret) > 0 {
  103. return ret, nil
  104. }
  105. return ret, err
  106. }
  107. }
  108. }