1//go:build darwin
2//+build darwin
3
4package pty
5
6import (
7	"errors"
8	"os"
9	"syscall"
10	"unsafe"
11)
12
13func open() (pty, tty *os.File, err error) {
14	pFD, err := syscall.Open("/dev/ptmx", syscall.O_RDWR|syscall.O_CLOEXEC, 0)
15	if err != nil {
16		return nil, nil, err
17	}
18	p := os.NewFile(uintptr(pFD), "/dev/ptmx")
19	// In case of error after this point, make sure we close the ptmx fd.
20	defer func() {
21		if err != nil {
22			_ = p.Close() // Best effort.
23		}
24	}()
25
26	sname, err := ptsname(p)
27	if err != nil {
28		return nil, nil, err
29	}
30
31	if err := grantpt(p); err != nil {
32		return nil, nil, err
33	}
34
35	if err := unlockpt(p); err != nil {
36		return nil, nil, err
37	}
38
39	t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
40	if err != nil {
41		return nil, nil, err
42	}
43	return p, t, nil
44}
45
46func ptsname(f *os.File) (string, error) {
47	n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
48
49	err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
50	if err != nil {
51		return "", err
52	}
53
54	for i, c := range n {
55		if c == 0 {
56			return string(n[:i]), nil
57		}
58	}
59	return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
60}
61
62func grantpt(f *os.File) error {
63	return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
64}
65
66func unlockpt(f *os.File) error {
67	return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
68}
69