pty_freebsd.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. package pty
  2. import (
  3. "errors"
  4. "os"
  5. "syscall"
  6. "unsafe"
  7. )
  8. func posixOpenpt(oflag int) (fd int, err error) {
  9. r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
  10. fd = int(r0)
  11. if e1 != 0 {
  12. err = e1
  13. }
  14. return fd, err
  15. }
  16. func open() (pty, tty *os.File, err error) {
  17. fd, err := posixOpenpt(syscall.O_RDWR | syscall.O_CLOEXEC)
  18. if err != nil {
  19. return nil, nil, err
  20. }
  21. p := os.NewFile(uintptr(fd), "/dev/pts")
  22. // In case of error after this point, make sure we close the pts fd.
  23. defer func() {
  24. if err != nil {
  25. _ = p.Close() // Best effort.
  26. }
  27. }()
  28. sname, err := ptsname(p)
  29. if err != nil {
  30. return nil, nil, err
  31. }
  32. t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
  33. if err != nil {
  34. return nil, nil, err
  35. }
  36. return p, t, nil
  37. }
  38. func isptmaster(fd uintptr) (bool, error) {
  39. err := ioctl(fd, syscall.TIOCPTMASTER, 0)
  40. return err == nil, err
  41. }
  42. var (
  43. emptyFiodgnameArg fiodgnameArg
  44. ioctlFIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
  45. )
  46. func ptsname(f *os.File) (string, error) {
  47. master, err := isptmaster(f.Fd())
  48. if err != nil {
  49. return "", err
  50. }
  51. if !master {
  52. return "", syscall.EINVAL
  53. }
  54. const n = _C_SPECNAMELEN + 1
  55. var (
  56. buf = make([]byte, n)
  57. arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
  58. )
  59. if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
  60. return "", err
  61. }
  62. for i, c := range buf {
  63. if c == 0 {
  64. return string(buf[:i]), nil
  65. }
  66. }
  67. return "", errors.New("FIODGNAME string not NUL-terminated")
  68. }