Skip to content

Commit 2916954

Browse files
committed
Do not set file descriptor into blocking mode
(*os.File).Fd() implicitly sets file descriptor into blocking mode [1], [2] We avoid that by calling (*os.File).SyscallConn instead. This method was added in go1.12 (released on 2019-02-25) [1]: https://pkg.go.dev/os#File.Fd [2]: https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/os/file_unix.go;l=80-87
1 parent 5c3f42a commit 2916954

12 files changed

+76
-33
lines changed

ioctl.go ioctl_inner.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const (
1010
TIOCSWINSZ = syscall.TIOCSWINSZ
1111
)
1212

13-
func ioctl(fd, cmd, ptr uintptr) error {
13+
func ioctl_inner(fd, cmd, ptr uintptr) error {
1414
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
1515
if e != 0 {
1616
return e

ioctl_outer.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//go:build !windows
2+
// +build !windows
3+
4+
//
5+
package pty
6+
7+
import "os"
8+
9+
func ioctl(f *os.File, cmd, ptr uintptr) error {
10+
sc, e := f.SyscallConn()
11+
if e != nil {
12+
return ioctl_inner(f.Fd(), cmd, ptr) // fall back to blocking io (old behavior)
13+
}
14+
15+
ch := make(chan error, 1)
16+
defer close(ch)
17+
18+
e = sc.Control(func(fd uintptr) { ch <- ioctl_inner(fd, cmd, ptr) })
19+
if e != nil {
20+
return e
21+
}
22+
e = <-ch
23+
return e
24+
}

ioctl_solaris.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type strioctl struct {
4040
// Defined in asm_solaris_amd64.s.
4141
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
4242

43-
func ioctl(fd, cmd, ptr uintptr) error {
43+
func ioctl_inner(fd, cmd, ptr uintptr) error {
4444
if _, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procioctl)), 3, fd, cmd, ptr, 0, 0, 0); errno != 0 {
4545
return errno
4646
}

ioctl_unsupported.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ const (
88
TIOCSWINSZ = 0
99
)
1010

11-
func ioctl(fd, cmd, ptr uintptr) error {
11+
func ioctl_inner(fd, cmd, ptr uintptr) error {
1212
return ErrUnsupported
1313
}

pty_darwin.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func open() (pty, tty *os.File, err error) {
4646
func ptsname(f *os.File) (string, error) {
4747
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
4848

49-
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
49+
err := ioctl(f, syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
5050
if err != nil {
5151
return "", err
5252
}
@@ -60,9 +60,9 @@ func ptsname(f *os.File) (string, error) {
6060
}
6161

6262
func grantpt(f *os.File) error {
63-
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
63+
return ioctl(f, syscall.TIOCPTYGRANT, 0)
6464
}
6565

6666
func unlockpt(f *os.File) error {
67-
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
67+
return ioctl(f, syscall.TIOCPTYUNLK, 0)
6868
}

pty_dragonfly.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,17 @@ func open() (pty, tty *os.File, err error) {
4545
}
4646

4747
func grantpt(f *os.File) error {
48-
_, err := isptmaster(f.Fd())
48+
_, err := isptmaster(f)
4949
return err
5050
}
5151

5252
func unlockpt(f *os.File) error {
53-
_, err := isptmaster(f.Fd())
53+
_, err := isptmaster(f)
5454
return err
5555
}
5656

57-
func isptmaster(fd uintptr) (bool, error) {
58-
err := ioctl(fd, syscall.TIOCISPTMASTER, 0)
57+
func isptmaster(f *os.File) (bool, error) {
58+
err := ioctl(f, syscall.TIOCISPTMASTER, 0)
5959
return err == nil, err
6060
}
6161

@@ -68,7 +68,7 @@ func ptsname(f *os.File) (string, error) {
6868
name := make([]byte, _C_SPECNAMELEN)
6969
fa := fiodgnameArg{Name: (*byte)(unsafe.Pointer(&name[0])), Len: _C_SPECNAMELEN, Pad_cgo_0: [4]byte{0, 0, 0, 0}}
7070

71-
err := ioctl(f.Fd(), ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
71+
err := ioctl(f, ioctl_FIODNAME, uintptr(unsafe.Pointer(&fa)))
7272
if err != nil {
7373
return "", err
7474
}

pty_freebsd.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func open() (pty, tty *os.File, err error) {
4444
return p, t, nil
4545
}
4646

47-
func isptmaster(fd uintptr) (bool, error) {
48-
err := ioctl(fd, syscall.TIOCPTMASTER, 0)
47+
func isptmaster(f *os.File) (bool, error) {
48+
err := ioctl(f, syscall.TIOCPTMASTER, 0)
4949
return err == nil, err
5050
}
5151

@@ -55,7 +55,7 @@ var (
5555
)
5656

5757
func ptsname(f *os.File) (string, error) {
58-
master, err := isptmaster(f.Fd())
58+
master, err := isptmaster(f)
5959
if err != nil {
6060
return "", err
6161
}
@@ -68,7 +68,7 @@ func ptsname(f *os.File) (string, error) {
6868
buf = make([]byte, n)
6969
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
7070
)
71-
if err := ioctl(f.Fd(), ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
71+
if err := ioctl(f, ioctlFIODGNAME, uintptr(unsafe.Pointer(&arg))); err != nil {
7272
return "", err
7373
}
7474

pty_linux.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func open() (pty, tty *os.File, err error) {
4040

4141
func ptsname(f *os.File) (string, error) {
4242
var n _C_uint
43-
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) //nolint:gosec // Expected unsafe pointer for Syscall call.
43+
err := ioctl(f, syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))) //nolint:gosec // Expected unsafe pointer for Syscall call.
4444
if err != nil {
4545
return "", err
4646
}
@@ -50,5 +50,5 @@ func ptsname(f *os.File) (string, error) {
5050
func unlockpt(f *os.File) error {
5151
var u _C_int
5252
// use TIOCSPTLCK with a pointer to zero to clear the lock
53-
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) //nolint:gosec // Expected unsafe pointer for Syscall call.
53+
return ioctl(f, syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) //nolint:gosec // Expected unsafe pointer for Syscall call.
5454
}

pty_netbsd.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func ptsname(f *os.File) (string, error) {
4747
* ioctl(fd, TIOCPTSNAME, &pm) == -1 ? NULL : pm.sn;
4848
*/
4949
var ptm ptmget
50-
if err := ioctl(f.Fd(), uintptr(ioctl_TIOCPTSNAME), uintptr(unsafe.Pointer(&ptm))); err != nil {
50+
if err := ioctl(f, uintptr(ioctl_TIOCPTSNAME), uintptr(unsafe.Pointer(&ptm))); err != nil {
5151
return "", err
5252
}
5353
name := make([]byte, len(ptm.Sn))
@@ -65,5 +65,5 @@ func grantpt(f *os.File) error {
6565
* from grantpt(3): Calling grantpt() is equivalent to:
6666
* ioctl(fd, TIOCGRANTPT, 0);
6767
*/
68-
return ioctl(f.Fd(), uintptr(ioctl_TIOCGRANTPT), 0)
68+
return ioctl(f, uintptr(ioctl_TIOCGRANTPT), 0)
6969
}

pty_openbsd.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func open() (pty, tty *os.File, err error) {
3636
defer p.Close()
3737

3838
var ptm ptmget
39-
if err := ioctl(p.Fd(), uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
39+
if err := ioctl(p, uintptr(ioctl_PTMGET), uintptr(unsafe.Pointer(&ptm))); err != nil {
4040
return nil, nil, err
4141
}
4242

pty_solaris.go

+30-11
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func open() (pty, tty *os.File, err error) {
6565
}
6666

6767
func ptsname(f *os.File) (string, error) {
68-
dev, err := ptsdev(f.Fd())
68+
dev, err := ptsdev(f)
6969
if err != nil {
7070
return "", err
7171
}
@@ -84,27 +84,46 @@ func unlockpt(f *os.File) error {
8484
icLen: 0,
8585
icDP: nil,
8686
}
87-
return ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr)))
87+
return ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr)))
8888
}
8989

9090
func minor(x uint64) uint64 { return x & 0377 }
9191

92-
func ptsdev(fd uintptr) (uint64, error) {
92+
func ptsdev(f *os.File) (uint64, error) {
9393
istr := strioctl{
9494
icCmd: ISPTM,
9595
icTimeout: 0,
9696
icLen: 0,
9797
icDP: nil,
9898
}
9999

100-
if err := ioctl(fd, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
100+
if err := ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
101101
return 0, err
102102
}
103-
var status syscall.Stat_t
104-
if err := syscall.Fstat(int(fd), &status); err != nil {
103+
var errors = make(chan error, 1)
104+
var results = make(chan uint64, 1)
105+
defer close(errors)
106+
defer close(results)
107+
108+
var err error
109+
var sc syscall.RawConn
110+
sc, err = f.SyscallConn()
111+
if err != nil {
112+
return 0, err
113+
}
114+
err = sc.Control(func(fd uintptr) {
115+
var status syscall.Stat_t
116+
if err := syscall.Fstat(int(fd), &status); err != nil {
117+
results <- 0
118+
errors <- err
119+
}
120+
results <- uint64(minor(status.Rdev))
121+
errors <- nil
122+
})
123+
if err != nil {
105124
return 0, err
106125
}
107-
return uint64(minor(status.Rdev)), nil
126+
return <-results, <-errors
108127
}
109128

110129
type ptOwn struct {
@@ -113,7 +132,7 @@ type ptOwn struct {
113132
}
114133

115134
func grantpt(f *os.File) error {
116-
if _, err := ptsdev(f.Fd()); err != nil {
135+
if _, err := ptsdev(f); err != nil {
117136
return err
118137
}
119138
pto := ptOwn{
@@ -127,7 +146,7 @@ func grantpt(f *os.File) error {
127146
icLen: int32(unsafe.Sizeof(strioctl{})),
128147
icDP: unsafe.Pointer(&pto),
129148
}
130-
if err := ioctl(f.Fd(), I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
149+
if err := ioctl(f, I_STR, uintptr(unsafe.Pointer(&istr))); err != nil {
131150
return errors.New("access denied")
132151
}
133152
return nil
@@ -145,8 +164,8 @@ func streamsPush(f *os.File, mod string) error {
145164
// but since we are not using libc or XPG4.2, we should not be
146165
// double-pushing modules
147166

148-
if err := ioctl(f.Fd(), I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil {
167+
if err := ioctl(f, I_FIND, uintptr(unsafe.Pointer(&buf[0]))); err != nil {
149168
return nil
150169
}
151-
return ioctl(f.Fd(), I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
170+
return ioctl(f, I_PUSH, uintptr(unsafe.Pointer(&buf[0])))
152171
}

winsize_unix.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ type Winsize struct {
2020
// Setsize resizes t to s.
2121
func Setsize(t *os.File, ws *Winsize) error {
2222
//nolint:gosec // Expected unsafe pointer for Syscall call.
23-
return ioctl(t.Fd(), syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws)))
23+
return ioctl(t, syscall.TIOCSWINSZ, uintptr(unsafe.Pointer(ws)))
2424
}
2525

2626
// GetsizeFull returns the full terminal size description.
2727
func GetsizeFull(t *os.File) (size *Winsize, err error) {
2828
var ws Winsize
2929

3030
//nolint:gosec // Expected unsafe pointer for Syscall call.
31-
if err := ioctl(t.Fd(), syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))); err != nil {
31+
if err := ioctl(t, syscall.TIOCGWINSZ, uintptr(unsafe.Pointer(&ws))); err != nil {
3232
return nil, err
3333
}
3434
return &ws, nil

0 commit comments

Comments
 (0)