mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-08-30 13:21:49 +00:00
usbip: hand the uevent netlink fd to the runtime poller
The raw blocking fd had three lifecycle bugs: close() does not wake a thread blocked in recvfrom, the watchdog and the error path both closed the fd without coordination, and a closed fd number could be recycled to an unrelated connection that the lingering reader would then consume from and the second close would tear down. Wrapping the nonblocking fd in os.File gives poller-based reads that Close wakes, idempotent close, and no reuse while a read is in flight.
This commit is contained in:
parent
05ac804e6c
commit
77e77fb481
1 changed files with 34 additions and 8 deletions
|
|
@ -4,18 +4,24 @@ package usbip
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const ueventReceiveBufferSize = 1 << 20
|
||||
|
||||
// ueventListener wraps the netlink socket in an *os.File so the fd is
|
||||
// owned by the Go runtime poller: Close wakes a goroutine blocked in
|
||||
// WaitUSBEvent, is idempotent, and the fd number cannot be recycled to
|
||||
// another connection while a read is still in flight — all of which a
|
||||
// raw fd with blocking Recvfrom gets wrong.
|
||||
type ueventListener struct {
|
||||
fd int
|
||||
file *os.File
|
||||
}
|
||||
|
||||
func newUEventListener() (*ueventListener, error) {
|
||||
fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_DGRAM, unix.NETLINK_KOBJECT_UEVENT)
|
||||
fd, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_DGRAM|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, unix.NETLINK_KOBJECT_UEVENT)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -29,23 +35,43 @@ func newUEventListener() (*ueventListener, error) {
|
|||
_ = unix.Close(fd)
|
||||
return nil, err
|
||||
}
|
||||
return &ueventListener{fd: fd}, nil
|
||||
return &ueventListener{file: os.NewFile(uintptr(fd), "netlink-uevent")}, nil
|
||||
}
|
||||
|
||||
func (l *ueventListener) Close() error {
|
||||
return unix.Close(l.fd)
|
||||
return l.file.Close()
|
||||
}
|
||||
|
||||
func (l *ueventListener) WaitUSBEvent() error {
|
||||
rawConn, err := l.file.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var buf [16384]byte
|
||||
for {
|
||||
n, from, err := unix.Recvfrom(l.fd, buf[:], 0)
|
||||
if err == unix.ENOBUFS {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
n int
|
||||
from unix.Sockaddr
|
||||
recvErr error
|
||||
)
|
||||
err = rawConn.Read(func(fd uintptr) bool {
|
||||
for {
|
||||
n, from, recvErr = unix.Recvfrom(int(fd), buf[:], 0)
|
||||
if recvErr == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
return recvErr != unix.EAGAIN
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if recvErr == unix.ENOBUFS {
|
||||
return nil
|
||||
}
|
||||
if recvErr != nil {
|
||||
return recvErr
|
||||
}
|
||||
if source, ok := from.(*unix.SockaddrNetlink); ok && source.Pid != 0 {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue