forked from rosa/hakurei
internal/netlink: nonblocking socket I/O
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.
|
// A conn represents resources associated to a netlink socket.
|
||||||
type conn struct {
|
type conn struct {
|
||||||
// AF_NETLINK socket.
|
// 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.
|
// Kernel module or netlink group to communicate with.
|
||||||
family int
|
family int
|
||||||
// Message sequence number.
|
// Message sequence number.
|
||||||
@@ -42,7 +44,7 @@ func dial(family int) (*conn, error) {
|
|||||||
var c conn
|
var c conn
|
||||||
if fd, err := syscall.Socket(
|
if fd, err := syscall.Socket(
|
||||||
syscall.AF_NETLINK,
|
syscall.AF_NETLINK,
|
||||||
syscall.SOCK_RAW|syscall.SOCK_CLOEXEC,
|
syscall.SOCK_RAW|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC,
|
||||||
family,
|
family,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, os.NewSyscallError("socket", err)
|
return nil, os.NewSyscallError("socket", err)
|
||||||
@@ -53,8 +55,14 @@ func dial(family int) (*conn, error) {
|
|||||||
_ = syscall.Close(fd)
|
_ = syscall.Close(fd)
|
||||||
return nil, os.NewSyscallError("bind", err)
|
return nil, os.NewSyscallError("bind", err)
|
||||||
} else {
|
} 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.pos = syscall.NLMSG_HDRLEN
|
||||||
c.buf = make([]byte, os.Getpagesize())
|
c.buf = make([]byte, os.Getpagesize())
|
||||||
return &c, nil
|
return &c, nil
|
||||||
@@ -66,7 +74,42 @@ func (c *conn) Close() error {
|
|||||||
return syscall.EINVAL
|
return syscall.EINVAL
|
||||||
}
|
}
|
||||||
c.buf = nil
|
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.
|
// 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 {
|
func (c *conn) receive(f HandlerFunc, flags int) error {
|
||||||
for {
|
for {
|
||||||
buf := c.buf
|
buf := c.buf
|
||||||
if n, _, err := syscall.Recvfrom(c.fd, buf, flags); err != nil {
|
if n, _, err := c.recvfrom(buf, flags); err != nil {
|
||||||
return os.NewSyscallError("recvfrom", err)
|
return err
|
||||||
} else if n < syscall.NLMSG_HDRLEN {
|
} else if n < syscall.NLMSG_HDRLEN {
|
||||||
return syscall.EBADE
|
return syscall.EBADE
|
||||||
} else {
|
} else {
|
||||||
@@ -187,10 +230,10 @@ func (c *conn) Roundtrip(f HandlerFunc) error {
|
|||||||
}
|
}
|
||||||
defer func() { c.seq++ }()
|
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,
|
Family: syscall.AF_NETLINK,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return os.NewSyscallError("sendto", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.receive(f, 0)
|
return c.receive(f, 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user