console_unix.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // +build darwin freebsd linux solaris
  2. package console
  3. import (
  4. "os"
  5. "golang.org/x/sys/unix"
  6. )
  7. // NewPty creates a new pty pair
  8. // The master is returned as the first console and a string
  9. // with the path to the pty slave is returned as the second
  10. func NewPty() (Console, string, error) {
  11. f, err := os.OpenFile("/dev/ptmx", unix.O_RDWR|unix.O_NOCTTY|unix.O_CLOEXEC, 0)
  12. if err != nil {
  13. return nil, "", err
  14. }
  15. slave, err := ptsname(f)
  16. if err != nil {
  17. return nil, "", err
  18. }
  19. if err := unlockpt(f); err != nil {
  20. return nil, "", err
  21. }
  22. m, err := newMaster(f)
  23. if err != nil {
  24. return nil, "", err
  25. }
  26. return m, slave, nil
  27. }
  28. type master struct {
  29. f *os.File
  30. original *unix.Termios
  31. }
  32. func (m *master) Read(b []byte) (int, error) {
  33. return m.f.Read(b)
  34. }
  35. func (m *master) Write(b []byte) (int, error) {
  36. return m.f.Write(b)
  37. }
  38. func (m *master) Close() error {
  39. return m.f.Close()
  40. }
  41. func (m *master) Resize(ws WinSize) error {
  42. return tcswinsz(m.f.Fd(), ws)
  43. }
  44. func (m *master) ResizeFrom(c Console) error {
  45. ws, err := c.Size()
  46. if err != nil {
  47. return err
  48. }
  49. return m.Resize(ws)
  50. }
  51. func (m *master) Reset() error {
  52. if m.original == nil {
  53. return nil
  54. }
  55. return tcset(m.f.Fd(), m.original)
  56. }
  57. func (m *master) getCurrent() (unix.Termios, error) {
  58. var termios unix.Termios
  59. if err := tcget(m.f.Fd(), &termios); err != nil {
  60. return unix.Termios{}, err
  61. }
  62. return termios, nil
  63. }
  64. func (m *master) SetRaw() error {
  65. rawState, err := m.getCurrent()
  66. if err != nil {
  67. return err
  68. }
  69. rawState = cfmakeraw(rawState)
  70. rawState.Oflag = rawState.Oflag | unix.OPOST
  71. return tcset(m.f.Fd(), &rawState)
  72. }
  73. func (m *master) DisableEcho() error {
  74. rawState, err := m.getCurrent()
  75. if err != nil {
  76. return err
  77. }
  78. rawState.Lflag = rawState.Lflag &^ unix.ECHO
  79. return tcset(m.f.Fd(), &rawState)
  80. }
  81. func (m *master) Size() (WinSize, error) {
  82. return tcgwinsz(m.f.Fd())
  83. }
  84. func (m *master) Fd() uintptr {
  85. return m.f.Fd()
  86. }
  87. func (m *master) Name() string {
  88. return m.f.Name()
  89. }
  90. // checkConsole checks if the provided file is a console
  91. func checkConsole(f *os.File) error {
  92. var termios unix.Termios
  93. if tcget(f.Fd(), &termios) != nil {
  94. return ErrNotAConsole
  95. }
  96. return nil
  97. }
  98. func newMaster(f *os.File) (Console, error) {
  99. m := &master{
  100. f: f,
  101. }
  102. t, err := m.getCurrent()
  103. if err != nil {
  104. return nil, err
  105. }
  106. m.original = &t
  107. return m, nil
  108. }
  109. // ClearONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
  110. // created by us acts normally. In particular, a not-very-well-known default of
  111. // Linux unix98 ptys is that they have +onlcr by default. While this isn't a
  112. // problem for terminal emulators, because we relay data from the terminal we
  113. // also relay that funky line discipline.
  114. func ClearONLCR(fd uintptr) error {
  115. return setONLCR(fd, false)
  116. }
  117. // SetONLCR sets the necessary tty_ioctl(4)s to ensure that a pty pair
  118. // created by us acts as intended for a terminal emulator.
  119. func SetONLCR(fd uintptr) error {
  120. return setONLCR(fd, true)
  121. }