internal/netlink: nonblocking socket I/O
All checks were successful
Test / Create distribution (push) Successful in 1m13s
Test / Sandbox (push) Successful in 2m59s
Test / Hakurei (push) Successful in 4m0s
Test / ShareFS (push) Successful in 4m1s
Test / Sandbox (race detector) (push) Successful in 5m21s
Test / Hakurei (race detector) (push) Successful in 3m21s
Test / Flake checks (push) Successful in 1m18s
All checks were successful
Test / Create distribution (push) Successful in 1m13s
Test / Sandbox (push) Successful in 2m59s
Test / Hakurei (push) Successful in 4m0s
Test / ShareFS (push) Successful in 4m1s
Test / Sandbox (race detector) (push) Successful in 5m21s
Test / Hakurei (race detector) (push) Successful in 3m21s
Test / Flake checks (push) Successful in 1m18s
This enables use with blocking calls like when used with NETLINK_KOBJECT_UEVENT. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -24,7 +24,9 @@ func getpid() uint32 {
|
||||
// A conn represents resources associated to a netlink socket.
|
||||
type conn struct {
|
||||
// AF_NETLINK socket.
|
||||
fd int
|
||||
f *os.File
|
||||
// For using runtime polling via f.
|
||||
raw syscall.RawConn
|
||||
// Kernel module or netlink group to communicate with.
|
||||
family int
|
||||
// Message sequence number.
|
||||
@@ -42,7 +44,7 @@ func dial(family int) (*conn, error) {
|
||||
var c conn
|
||||
if fd, err := syscall.Socket(
|
||||
syscall.AF_NETLINK,
|
||||
syscall.SOCK_RAW|syscall.SOCK_CLOEXEC,
|
||||
syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC,
|
||||
family,
|
||||
); err != nil {
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
@@ -53,8 +55,14 @@ func dial(family int) (*conn, error) {
|
||||
_ = syscall.Close(fd)
|
||||
return nil, os.NewSyscallError("bind", err)
|
||||
} else {
|
||||
c.fd, c.family = fd, family
|
||||
c.family = family
|
||||
c.f = os.NewFile(uintptr(fd), "netlink")
|
||||
if c.raw, err = c.f.SyscallConn(); err != nil {
|
||||
_ = c.f.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c.pos = syscall.NLMSG_HDRLEN
|
||||
c.buf = make([]byte, os.Getpagesize())
|
||||
return &c, nil
|
||||
@@ -66,7 +74,42 @@ func (c *conn) Close() error {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
c.buf = nil
|
||||
return syscall.Close(c.fd)
|
||||
return c.f.Close()
|
||||
}
|
||||
|
||||
// recvfrom wraps recv(2) with nonblocking behaviour via the runtime network poller.
|
||||
func (c *conn) recvfrom(
|
||||
p []byte,
|
||||
flags int,
|
||||
) (n int, from syscall.Sockaddr, err error) {
|
||||
rcErr := c.raw.Read(func(fd uintptr) (done bool) {
|
||||
n, from, err = syscall.Recvfrom(int(fd), p, flags)
|
||||
return err != syscall.EWOULDBLOCK
|
||||
})
|
||||
if err != nil {
|
||||
err = os.NewSyscallError("recvfrom", err)
|
||||
} else {
|
||||
err = rcErr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// sendto wraps send(2) with nonblocking behaviour via the runtime network poller.
|
||||
func (c *conn) sendto(
|
||||
p []byte,
|
||||
flags int,
|
||||
to syscall.Sockaddr,
|
||||
) (err error) {
|
||||
rcErr := c.raw.Write(func(fd uintptr) (done bool) {
|
||||
err = syscall.Sendto(int(fd), p, flags, to)
|
||||
return err != syscall.EWOULDBLOCK
|
||||
})
|
||||
if err != nil {
|
||||
err = os.NewSyscallError("sendto", err)
|
||||
} else {
|
||||
err = rcErr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Msg is type constraint for types sent over the wire via netlink.
|
||||
@@ -152,8 +195,8 @@ type HandlerFunc func(resp []syscall.NetlinkMessage) error
|
||||
func (c *conn) receive(f HandlerFunc, flags int) error {
|
||||
for {
|
||||
buf := c.buf
|
||||
if n, _, err := syscall.Recvfrom(c.fd, buf, flags); err != nil {
|
||||
return os.NewSyscallError("recvfrom", err)
|
||||
if n, _, err := c.recvfrom(buf, flags); err != nil {
|
||||
return err
|
||||
} else if n < syscall.NLMSG_HDRLEN {
|
||||
return syscall.EBADE
|
||||
} else {
|
||||
@@ -187,10 +230,10 @@ func (c *conn) Roundtrip(f HandlerFunc) error {
|
||||
}
|
||||
defer func() { c.seq++ }()
|
||||
|
||||
if err := syscall.Sendto(c.fd, c.pending(), 0, &syscall.SockaddrNetlink{
|
||||
if err := c.sendto(c.pending(), 0, &syscall.SockaddrNetlink{
|
||||
Family: syscall.AF_NETLINK,
|
||||
}); err != nil {
|
||||
return os.NewSyscallError("sendto", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return c.receive(f, 0)
|
||||
|
||||
Reference in New Issue
Block a user