diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-03-28 01:06:17 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-03-28 15:39:16 +0900 |
| commit | ea014d6af2d1481ba05ffed2dddffeed370f6622 (patch) | |
| tree | 39242cb6c7d480312cee1501b202df46edde09de /internal/uevent/uevent.go | |
| parent | 1b48484c169daa48a006f52f48766d358fa8f2bc (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.go | 55 |
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 + } +} |
