aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/uevent/uevent.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/uevent/uevent.go')
-rw-r--r--internal/uevent/uevent.go118
1 files changed, 117 insertions, 1 deletions
diff --git a/internal/uevent/uevent.go b/internal/uevent/uevent.go
index 2c37172f..0e85c4cf 100644
--- a/internal/uevent/uevent.go
+++ b/internal/uevent/uevent.go
@@ -5,10 +5,14 @@ package uevent
import (
"context"
+ "encoding"
+ "encoding/hex"
"errors"
+ "fmt"
"strconv"
"sync/atomic"
"syscall"
+ "unsafe"
"hakurei.app/internal/netlink"
)
@@ -121,6 +125,117 @@ func (c *Conn) receiveEvent(ctx context.Context) (*Message, error) {
return &msg, err
}
+// UUID represents the value of SYNTH_UUID.
+//
+// This is not a generic UUID implementation. Do not attempt to use it for
+// anything other than passing and interpreting the SYNTH_UUID environment
+// variable of a uevent.
+type UUID [16]byte
+
+const (
+ // SizeUUID is the fixed size of string representation of [UUID] according
+ // to Documentation/ABI/testing/sysfs-uevent.
+ SizeUUID = 4 + len(UUID{})*2
+
+ // UUIDSep is the separator byte of [UUID].
+ UUIDSep = '-'
+)
+
+var (
+ _ encoding.TextAppender = new(UUID)
+ _ encoding.TextMarshaler = new(UUID)
+ _ encoding.TextUnmarshaler = new(UUID)
+)
+
+// String formats uuid according to Documentation/ABI/testing/sysfs-uevent.
+func (uuid *UUID) String() string {
+ s := make([]byte, 0, SizeUUID)
+ s = hex.AppendEncode(s, uuid[:4])
+ s = append(s, UUIDSep)
+ s = hex.AppendEncode(s, uuid[4:6])
+ s = append(s, UUIDSep)
+ s = hex.AppendEncode(s, uuid[6:8])
+ s = append(s, UUIDSep)
+ s = hex.AppendEncode(s, uuid[8:10])
+ s = append(s, UUIDSep)
+ s = hex.AppendEncode(s, uuid[10:16])
+ return unsafe.String(unsafe.SliceData(s), len(s))
+}
+
+func (uuid *UUID) AppendText(data []byte) ([]byte, error) {
+ return append(data, uuid.String()...), nil
+}
+func (uuid *UUID) MarshalText() ([]byte, error) {
+ return uuid.AppendText(nil)
+}
+
+var (
+ // ErrAutoUUID is returned parsing a SYNTH_UUID generated by the kernel for
+ // a synthetic event without a UUID passed in.
+ ErrAutoUUID = errors.New("UUID is not passed in")
+)
+
+// UUIDSizeError describes an incorrectly sized string representation of [UUID].
+type UUIDSizeError int
+
+func (e UUIDSizeError) Error() string {
+ return "got " + strconv.Itoa(int(e)) + " bytes " +
+ "instead of " + strconv.Itoa(SizeUUID)
+}
+
+// UUIDSeparatorError is an invalid separator in a malformed string
+// representation of [UUID].
+type UUIDSeparatorError byte
+
+func (e UUIDSeparatorError) Error() string {
+ return fmt.Sprintf("invalid UUID separator: %#U", rune(e))
+}
+
+// UnmarshalText parses data according to Documentation/ABI/testing/sysfs-uevent.
+func (uuid *UUID) UnmarshalText(data []byte) (err error) {
+ if len(data) == 1 && data[0] == '0' {
+ return ErrAutoUUID
+ }
+ if len(data) != SizeUUID {
+ return UUIDSizeError(len(data))
+ }
+
+ if _, err = hex.Decode(uuid[:], data[:8]); err != nil {
+ return
+ }
+ if data[8] != UUIDSep {
+ return UUIDSeparatorError(data[8])
+ }
+ data = data[9:]
+
+ if _, err = hex.Decode(uuid[4:], data[:4]); err != nil {
+ return
+ }
+ if data[4] != UUIDSep {
+ return UUIDSeparatorError(data[4])
+ }
+ data = data[5:]
+
+ if _, err = hex.Decode(uuid[6:], data[:4]); err != nil {
+ return
+ }
+ if data[4] != UUIDSep {
+ return UUIDSeparatorError(data[4])
+ }
+ data = data[5:]
+
+ if _, err = hex.Decode(uuid[8:], data[:4]); err != nil {
+ return
+ }
+ if data[4] != UUIDSep {
+ return UUIDSeparatorError(data[4])
+ }
+ data = data[5:]
+
+ _, err = hex.Decode(uuid[10:], data)
+ return
+}
+
// Consume continuously receives and parses events from the kernel and handles
// [Recoverable] and [NeedsColdboot] errors via caller-supplied functions,
// entering coldboot when required.
@@ -145,6 +260,7 @@ func (c *Conn) receiveEvent(ctx context.Context) (*Message, error) {
func (c *Conn) Consume(
ctx context.Context,
sysfs string,
+ uuid *UUID,
events chan<- *Message,
coldboot bool,
@@ -193,7 +309,7 @@ coldboot:
ctxColdboot, cancelColdboot := context.WithCancel(ctx)
var coldbootErr error
go func() {
- coldbootErr = Coldboot(ctxColdboot, sysfs, visited, handleWalkErr)
+ coldbootErr = Coldboot(ctxColdboot, sysfs, uuid, visited, handleWalkErr)
close(visited)
}()
for pathname := range visited {