sock_supported.go

 1//go:build (linux || darwin || windows) && !tinygo
 2
 3package sysfs
 4
 5import (
 6	"net"
 7	"syscall"
 8
 9	experimentalsys "github.com/tetratelabs/wazero/experimental/sys"
10	"github.com/tetratelabs/wazero/internal/fsapi"
11	socketapi "github.com/tetratelabs/wazero/internal/sock"
12)
13
14// Accept implements the same method as documented on socketapi.TCPSock
15func (f *tcpListenerFile) Accept() (socketapi.TCPConn, experimentalsys.Errno) {
16	// Ensure we have an incoming connection, otherwise return immediately.
17	if f.nonblock {
18		if ready, errno := _pollSock(f.tl, fsapi.POLLIN, 0); !ready || errno != 0 {
19			return nil, experimentalsys.EAGAIN
20		}
21	}
22
23	// Accept normally blocks goroutines, but we
24	// made sure that we have an incoming connection,
25	// so we should be safe.
26	if conn, err := f.tl.Accept(); err != nil {
27		return nil, experimentalsys.UnwrapOSError(err)
28	} else {
29		return newTcpConn(conn.(*net.TCPConn)), 0
30	}
31}
32
33// SetNonblock implements the same method as documented on fsapi.File
34func (f *tcpListenerFile) SetNonblock(enabled bool) (errno experimentalsys.Errno) {
35	f.nonblock = enabled
36	_, errno = syscallConnControl(f.tl, func(fd uintptr) (int, experimentalsys.Errno) {
37		return 0, setNonblockSocket(fd, enabled)
38	})
39	return
40}
41
42// Shutdown implements the same method as documented on experimentalsys.Conn
43func (f *tcpConnFile) Shutdown(how int) experimentalsys.Errno {
44	// FIXME: can userland shutdown listeners?
45	var err error
46	switch how {
47	case socketapi.SHUT_RD:
48		err = f.tc.CloseRead()
49	case socketapi.SHUT_WR:
50		err = f.tc.CloseWrite()
51	case socketapi.SHUT_RDWR:
52		return f.close()
53	default:
54		return experimentalsys.EINVAL
55	}
56	return experimentalsys.UnwrapOSError(err)
57}
58
59// syscallConnControl extracts a syscall.RawConn from the given syscall.Conn and applies
60// the given fn to a file descriptor, returning an integer or a nonzero syscall.Errno on failure.
61//
62// syscallConnControl streamlines the pattern of extracting the syscall.Rawconn,
63// invoking its syscall.RawConn.Control method, then handling properly the errors that may occur
64// within fn or returned by syscall.RawConn.Control itself.
65func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, experimentalsys.Errno)) (n int, errno experimentalsys.Errno) {
66	syscallConn, err := conn.SyscallConn()
67	if err != nil {
68		return 0, experimentalsys.UnwrapOSError(err)
69	}
70	// Prioritize the inner errno over Control
71	if controlErr := syscallConn.Control(func(fd uintptr) {
72		n, errno = fn(fd)
73	}); errno == 0 {
74		errno = experimentalsys.UnwrapOSError(controlErr)
75	}
76	return
77}