diff options
Diffstat (limited to 'internal/uevent/uevent.go')
| -rw-r--r-- | internal/uevent/uevent.go | 118 |
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 { |
