forked from rosa/hakurei
internal/kobject: process uevent message
This deals with environment variables generally present in every message. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
90
internal/kobject/event.go
Normal file
90
internal/kobject/event.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package kobject
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"hakurei.app/internal/uevent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Event is a [uevent.Message] with known environment variables processed.
|
||||||
|
type Event struct {
|
||||||
|
// alloc_uevent_skb: action_string
|
||||||
|
Action uevent.KobjectAction `json:"action"`
|
||||||
|
// alloc_uevent_skb: devpath
|
||||||
|
DevPath string `json:"devpath"`
|
||||||
|
|
||||||
|
// Uninterpreted environment variable pairs. An entry missing a separator
|
||||||
|
// gains the value "\x00".
|
||||||
|
Env map[string]string `json:"env"`
|
||||||
|
|
||||||
|
// SEQNUM value set by the kernel.
|
||||||
|
Sequence uint64 `json:"seqnum"`
|
||||||
|
// SYNTH_UUID value set on trigger, nil denotes a non-synthetic event.
|
||||||
|
Synth *uevent.UUID `json:"synth_uuid,omitempty"`
|
||||||
|
// SUBSYSTEM value set by the kernel.
|
||||||
|
Subsystem string `json:"subsystem"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate populates e with the contents of a [uevent.Message].
|
||||||
|
//
|
||||||
|
// The ACTION and DEVPATH environment variables are ignored and assumed to be
|
||||||
|
// consistent with the header.
|
||||||
|
func (e *Event) Populate(reportErr func(error), m *uevent.Message) {
|
||||||
|
if reportErr == nil {
|
||||||
|
reportErr = func(error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
*e = Event{
|
||||||
|
Action: m.Action,
|
||||||
|
DevPath: m.DevPath,
|
||||||
|
Env: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range m.Env {
|
||||||
|
k, v, ok := strings.Cut(s, "=")
|
||||||
|
if !ok {
|
||||||
|
if _, ok = e.Env[s]; !ok {
|
||||||
|
e.Env[s] = "\x00"
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch k {
|
||||||
|
case "ACTION", "DEVPATH":
|
||||||
|
continue
|
||||||
|
|
||||||
|
case "SEQNUM":
|
||||||
|
seq, err := strconv.ParseUint(v, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
if _e := errors.Unwrap(err); _e != nil {
|
||||||
|
err = _e
|
||||||
|
}
|
||||||
|
reportErr(err)
|
||||||
|
|
||||||
|
e.Env[k] = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
e.Sequence = seq
|
||||||
|
|
||||||
|
case "SYNTH_UUID":
|
||||||
|
var uuid uevent.UUID
|
||||||
|
err := uuid.UnmarshalText(unsafe.Slice(unsafe.StringData(v), len(v)))
|
||||||
|
if err != nil {
|
||||||
|
reportErr(err)
|
||||||
|
|
||||||
|
e.Env[k] = v
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
e.Synth = &uuid
|
||||||
|
|
||||||
|
case "SUBSYSTEM":
|
||||||
|
e.Subsystem = v
|
||||||
|
|
||||||
|
default:
|
||||||
|
e.Env[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
internal/kobject/event_test.go
Normal file
92
internal/kobject/event_test.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package kobject_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/internal/kobject"
|
||||||
|
"hakurei.app/internal/uevent"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEvent(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
msg uevent.Message
|
||||||
|
want kobject.Event
|
||||||
|
errs []error
|
||||||
|
}{
|
||||||
|
{"sample coldboot qemu", uevent.Message{
|
||||||
|
Action: uevent.KOBJ_ADD,
|
||||||
|
DevPath: "/devices/LNXSYSTM:00/LNXPWRBN:00",
|
||||||
|
Env: []string{
|
||||||
|
"ACTION=add",
|
||||||
|
"DEVPATH=/devices/LNXSYSTM:00/LNXPWRBN:00",
|
||||||
|
"SUBSYSTEM=acpi",
|
||||||
|
"SYNTH_UUID=fe4d7c9d-b8c6-4a70-9ef1-3d8a58d18eed",
|
||||||
|
"MODALIAS=acpi:LNXPWRBN:",
|
||||||
|
"SEQNUM=777",
|
||||||
|
}}, kobject.Event{
|
||||||
|
Action: uevent.KOBJ_ADD,
|
||||||
|
DevPath: "/devices/LNXSYSTM:00/LNXPWRBN:00",
|
||||||
|
Env: map[string]string{
|
||||||
|
"MODALIAS": "acpi:LNXPWRBN:",
|
||||||
|
},
|
||||||
|
Sequence: 777,
|
||||||
|
Synth: &uevent.UUID{
|
||||||
|
0xfe, 0x4d, 0x7c, 0x9d,
|
||||||
|
0xb8, 0xc6,
|
||||||
|
0x4a, 0x70,
|
||||||
|
0x9e, 0xf1,
|
||||||
|
0x3d, 0x8a, 0x58, 0xd1, 0x8e, 0xed,
|
||||||
|
},
|
||||||
|
Subsystem: "acpi",
|
||||||
|
}, []error{}},
|
||||||
|
|
||||||
|
{"nil reportErr", uevent.Message{Env: []string{
|
||||||
|
"SEQNUM=\x00",
|
||||||
|
}}, kobject.Event{Env: map[string]string{
|
||||||
|
"SEQNUM": "\x00",
|
||||||
|
}}, nil},
|
||||||
|
|
||||||
|
{"bad SEQNUM SYNTH_UUID", uevent.Message{Env: []string{
|
||||||
|
"SEQNUM=\x00",
|
||||||
|
"SYNTH_UUID=\x00",
|
||||||
|
"SUBSYSTEM=\x00",
|
||||||
|
}}, kobject.Event{Subsystem: "\x00", Env: map[string]string{
|
||||||
|
"SEQNUM": "\x00",
|
||||||
|
"SYNTH_UUID": "\x00",
|
||||||
|
}}, []error{strconv.ErrSyntax, uevent.UUIDSizeError(1)}},
|
||||||
|
|
||||||
|
{"bad sep", uevent.Message{Env: []string{
|
||||||
|
"SYNTH_UUID",
|
||||||
|
}}, kobject.Event{Env: map[string]string{
|
||||||
|
"SYNTH_UUID": "\x00",
|
||||||
|
}}, []error{}},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var f func(error)
|
||||||
|
gotErrs := make([]error, 0)
|
||||||
|
if tc.errs != nil {
|
||||||
|
f = func(err error) {
|
||||||
|
gotErrs = append(gotErrs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var got kobject.Event
|
||||||
|
got.Populate(f, &tc.msg)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(&got, &tc.want) {
|
||||||
|
t.Errorf("Populate: %#v, want %#v", got, tc.want)
|
||||||
|
}
|
||||||
|
if tc.errs != nil && !reflect.DeepEqual(gotErrs, tc.errs) {
|
||||||
|
t.Errorf("Populate: errs = %v, want %v", gotErrs, tc.errs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user