aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/uevent/uevent.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-03-28 01:06:17 +0900
committerOphestra <cat@gensokyo.uk>2026-03-28 15:39:16 +0900
commitea014d6af2d1481ba05ffed2dddffeed370f6622 (patch)
tree39242cb6c7d480312cee1501b202df46edde09de /internal/uevent/uevent.go
parent1b48484c169daa48a006f52f48766d358fa8f2bc (diff)
internal/uevent: consume kernel-originated events
These are not possible to cover outside integration vm. Extreme care is required when dealing with this method, so keep it simple. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/uevent/uevent.go')
-rw-r--r--internal/uevent/uevent.go55
1 files changed, 55 insertions, 0 deletions
diff --git a/internal/uevent/uevent.go b/internal/uevent/uevent.go
index 12754f46..61c0216e 100644
--- a/internal/uevent/uevent.go
+++ b/internal/uevent/uevent.go
@@ -4,6 +4,9 @@
package uevent
import (
+ "context"
+ "errors"
+ "strconv"
"sync/atomic"
"syscall"
@@ -48,3 +51,55 @@ func Dial() (*Conn, error) {
}
return &Conn{conn: c}, err
}
+
+var (
+ // ErrBadSocket is returned by [Conn.Consume] for a reply from a
+ // syscall.Sockaddr with unexpected concrete type.
+ ErrBadSocket = errors.New("unexpected socket address")
+)
+
+// BadPortError is returned by [Conn.Consume] upon receiving a message that did
+// not come from the kernel.
+type BadPortError syscall.SockaddrNetlink
+
+var _ Recoverable = new(BadPortError)
+
+func (*BadPortError) recoverable() {}
+func (e *BadPortError) Error() string {
+ return "unexpected message from port id " + strconv.Itoa(int(e.Pid)) +
+ " on NETLINK_KOBJECT_UEVENT"
+}
+
+// Consume continuously receives and parses events from the kernel. It returns
+// the first error it encounters.
+//
+// Callers must not restart event processing after a non-nil error that does not
+// satisfy [Recoverable] is returned.
+func (c *Conn) Consume(ctx context.Context, events chan<- *Message) error {
+ if err := c.enterExcl(); err != nil {
+ return err
+ }
+ defer c.exitExcl()
+
+ for {
+ data, from, err := c.conn.Recvfrom(ctx, 0)
+ if err != nil {
+ return err
+ }
+
+ // lib/kobject_uevent.c:
+ // set portid 0 to inform userspace message comes from kernel
+ if v, ok := from.(*syscall.SockaddrNetlink); !ok {
+ return ErrBadSocket
+ } else if v.Pid != 0 {
+ return (*BadPortError)(v)
+
+ }
+
+ var msg Message
+ if err = msg.UnmarshalBinary(data); err != nil {
+ return err
+ }
+ events <- &msg
+ }
+}