diff options
Diffstat (limited to 'internal/kobject/kobject.go')
| -rw-r--r-- | internal/kobject/kobject.go | 184 |
1 files changed, 101 insertions, 83 deletions
diff --git a/internal/kobject/kobject.go b/internal/kobject/kobject.go index 72d8f052..26359114 100644 --- a/internal/kobject/kobject.go +++ b/internal/kobject/kobject.go @@ -9,6 +9,7 @@ import ( "strconv" "sync" + "hakurei.app/internal/report" "hakurei.app/internal/uevent" ) @@ -29,9 +30,9 @@ const ( // Object represents a kernel object. type Object struct { // Origin of the object. - State int `json:"-"` + State int `json:"state,omitempty"` // Set by [uevent.KOBJ_OFFLINE] and [uevent.KOBJ_ONLINE]. - Offline bool + Offline bool `json:"offline,omitempty"` // alloc_uevent_skb: devpath DevPath string `json:"devpath"` @@ -48,11 +49,11 @@ type Object struct { Env map[string]string `json:"env"` } -// Clone returns a copy of o. -func (o *Object) Clone() Object { +// Clone returns the address of a copy of o. +func (o *Object) Clone() *Object { v := *o v.Env = maps.Clone(o.Env) - return v + return &v } // GoString returns compound literal for the underlying value. @@ -194,77 +195,94 @@ func (s *State) Range(ctx context.Context, f func(o *Object) bool) { } } -// UnexpectedColdbootError is reported by [State.Consume] for a coldboot event -// with action other than the expected [uevent.KOBJ_ADD]. -type UnexpectedColdbootError Event - -func (e UnexpectedColdbootError) Error() string { - return "unexpected " + e.Action.String() + " coldboot event" -} - -// DuplicateAddError is reported by [State.Consume] for a [uevent.KOBJ_ADD] -// event on a still-existing entry that was not the result of a coldboot. -type DuplicateAddError Event - -func (e DuplicateAddError) Error() string { - return "duplicate add event on devpath " + strconv.Quote(e.DevPath) +// An EventError describes a malformed or inconsistent [Event]. +type EventError struct { + Kind int `json:"fault"` + E Event `json:"event"` + O *Object `json:"object,omitempty"` } -// TargetError is reported by [State.Consume] for an event on a nonexistent -// entry. This is generally only possible before coldboot completes. -type TargetError Event - -func (e TargetError) Error() string { - return "unexpected " + e.Action.String() + - " event on devpath " + strconv.Quote(e.DevPath) -} - -// RemoveStateError is reported by [State.Consume] for a [uevent.KOBJ_REMOVE] -// event targeting an entry in a state other than [StateColdboot] and [StateNew]. -type RemoveStateError Object - -func (e RemoveStateError) Error() string { - return "remove event targeting devpath " + strconv.Quote(e.DevPath) + - " in state " + strconv.Itoa(e.State) -} - -// BindStateError is reported by [State.Consume] for a [uevent.KOBJ_BIND] event -// targeting an entry in a state other than [StateColdboot] and [StateNew]. -type BindStateError Object - -func (e BindStateError) Error() string { - return "bind event targeting devpath " + strconv.Quote(e.DevPath) + - " in state " + strconv.Itoa(e.State) -} +var _ report.RepresentableError = EventError{} -// UnbindStateError is reported by [State.Consume] for a [uevent.KOBJ_UNBIND] -// event targeting an entry in a state other than [StateBound]. -type UnbindStateError Object +func (EventError) Representable() {} -func (e UnbindStateError) Error() string { - return "unbind event targeting devpath " + strconv.Quote(e.DevPath) + - " in state " + strconv.Itoa(e.State) -} +const ( + // EUnexpectedColdboot is reported for a coldboot event with action other + // than the expected [uevent.KOBJ_ADD]. + EUnexpectedColdboot = iota + // EDuplicateAdd is reported for a [uevent.KOBJ_ADD] event on a + // still-existing entry that was not the result of a coldboot. + EDuplicateAdd + // EBadTarget is reported for an event on a nonexistent [Object]. This is + // generally only possible before coldboot completes. + EBadTarget + // ERemoveState is reported for a [uevent.KOBJ_REMOVE] event targeting an + // entry in a state other than [StateColdboot] and [StateNew]. + ERemoveState + // EUnexpectedOffline is reported for a [uevent.KOBJ_OFFLINE] or + // [uevent.KOBJ_ONLINE] event targeting an already offline or online object. + EUnexpectedOffline + // EBindState is reported for a [uevent.KOBJ_BIND] event targeting an entry + // in a state other than [StateColdboot] and [StateNew]. + EBindState + // EUnbindState is reported for a [uevent.KOBJ_UNBIND] event targeting an + // entry in a state other than [StateBound]. + EUnbindState + // EMalformedMove is reported for a [uevent.KOBJ_MOVE] event missing the + // DEVPATH_OLD environment variable. + EMalformedMove +) -// MalformedMoveError is reported by [State.Consume] for a [uevent.KOBJ_MOVE] -// event missing the DEVPATH_OLD environment variable. -type MalformedMoveError Event +func (e EventError) Error() string { + switch e.Kind { + case EUnexpectedColdboot: + return "unexpected " + e.E.Action.String() + " coldboot event" + case EDuplicateAdd: + return "duplicate add event on devpath " + strconv.Quote(e.E.DevPath) + case EBadTarget: + return "unexpected " + e.E.Action.String() + " event on devpath " + + strconv.Quote(e.E.DevPath) + case ERemoveState: + if e.O == nil { + return "invalid remove event error" + } + return "remove event targeting devpath " + strconv.Quote(e.E.DevPath) + + " in state " + strconv.Itoa(e.O.State) + case EUnexpectedOffline: + if e.O == nil { + return "invalid unexpected offline error" + } + if e.O.Offline { + return "offline event targeting devpath " + strconv.Quote(e.E.DevPath) + } + return "online event targeting devpath " + strconv.Quote(e.E.DevPath) + case EBindState: + if e.O == nil { + return "invalid bind state error" + } + return "bind event targeting devpath " + strconv.Quote(e.E.DevPath) + + " in state " + strconv.Itoa(e.O.State) + case EUnbindState: + if e.O == nil { + return "invalid unbind state error" + } + return "unbind event targeting devpath " + strconv.Quote(e.E.DevPath) + + " in state " + strconv.Itoa(e.O.State) + case EMalformedMove: + return "move event targeting devpath " + strconv.Quote(e.E.DevPath) + + " missing DEVPATH_OLD" -func (e MalformedMoveError) Error() string { - return "move event targeting devpath " + strconv.Quote(e.DevPath) + - " missing DEVPATH_OLD" + default: + return "invalid event error kind " + strconv.Itoa(e.Kind) + } } -// UnexpectedOfflineError is reported by [State.Consume] for a -// [uevent.KOBJ_OFFLINE] or [uevent.KOBJ_ONLINE] event targeting an already -// offline or online object. -type UnexpectedOfflineError Object - -func (e UnexpectedOfflineError) Error() string { - if e.Offline { - return "offline event targeting devpath " + strconv.Quote(e.DevPath) +// NewError returns a new [EventError] for e and o. +func (e *Event) NewError(kind int, o *Object) error { + if o != nil { + o = o.Clone() } - return "online event targeting devpath " + strconv.Quote(e.DevPath) + return EventError{kind, e.Clone(), o} } // processEvent merges an event into s. @@ -274,7 +292,7 @@ func (s *State) processEvent(e *Event) { coldboot := e.Synth != nil if e.Action != uevent.KOBJ_ADD && coldboot { - s.reportErr(UnexpectedColdbootError(e.Clone())) + s.reportErr(e.NewError(EUnexpectedColdboot, nil)) return } @@ -282,7 +300,7 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_ADD: if e.Synth == nil { if o, ok := s.uevent[e.DevPath]; ok { - s.reportErr(DuplicateAddError(e.Clone())) + s.reportErr(e.NewError(EDuplicateAdd, o)) o.merge(e.Env) s.dispatchIter(o) return @@ -299,10 +317,10 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_REMOVE: if o, ok := s.uevent[e.DevPath]; !ok { - s.reportErr(TargetError(e.Clone())) + s.reportErr(e.NewError(EBadTarget, nil)) return } else if o.State != StateColdboot && o.State != StateNew { - s.reportErr(RemoveStateError(o.Clone())) + s.reportErr(e.NewError(ERemoveState, o)) } delete(s.uevent, e.DevPath) return @@ -310,7 +328,7 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_CHANGE: o, ok := s.uevent[e.DevPath] if !ok { - s.reportErr(TargetError(e.Clone())) + s.reportErr(e.NewError(EBadTarget, nil)) // this suffers from the coldboot race window similar to KOBJ_MOVE, // however this action combines driver-specific and change-specific // environment variables and combines them with environment @@ -333,11 +351,11 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_MOVE: var o *Object if old, ok := e.Env["DEVPATH_OLD"]; !ok { - s.reportErr(MalformedMoveError(e.Clone())) + s.reportErr(e.NewError(EMalformedMove, nil)) // not reached o = e.makeColdboot() } else if o, ok = s.uevent[old]; !ok { - s.reportErr(TargetError(e.Clone())) + s.reportErr(e.NewError(EBadTarget, nil)) // this generally happens during coldboot, dropping the event here // may cause inconsistent state if the coldboot event for this // object was generated before the bind event @@ -356,14 +374,14 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_ONLINE: o, ok := s.uevent[e.DevPath] if !ok { - s.reportErr(TargetError(e.Clone())) + s.reportErr(e.NewError(EBadTarget, nil)) // coldboot race window similar to an unexpected KOBJ_MOVE o = e.makeColdboot() s.uevent[e.DevPath] = o o.merge(e.Env) } if !o.Offline { - s.reportErr(UnexpectedOfflineError(o.Clone())) + s.reportErr(e.NewError(EUnexpectedOffline, o)) } o.Offline = false s.dispatchIter(o) @@ -372,14 +390,14 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_OFFLINE: o, ok := s.uevent[e.DevPath] if !ok { - s.reportErr(TargetError(e.Clone())) + s.reportErr(e.NewError(EBadTarget, nil)) // coldboot race window similar to an unexpected KOBJ_MOVE o = e.makeColdboot() s.uevent[e.DevPath] = o o.merge(e.Env) } if o.Offline { - s.reportErr(UnexpectedOfflineError(o.Clone())) + s.reportErr(e.NewError(EUnexpectedOffline, o)) } o.Offline = true s.dispatchIter(o) @@ -388,13 +406,13 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_BIND: o, ok := s.uevent[e.DevPath] if !ok { - s.reportErr(TargetError(e.Clone())) + s.reportErr(e.NewError(EBadTarget, nil)) // coldboot race window similar to an unexpected KOBJ_MOVE o = e.makeColdboot() s.uevent[e.DevPath] = o } if o.State != StateColdboot && o.State != StateNew { - s.reportErr(BindStateError(o.Clone())) + s.reportErr(e.NewError(EBindState, o)) } o.State = StateBound o.merge(e.Env) @@ -404,13 +422,13 @@ func (s *State) processEvent(e *Event) { case uevent.KOBJ_UNBIND: o, ok := s.uevent[e.DevPath] if !ok { - s.reportErr(TargetError(e.Clone())) + s.reportErr(e.NewError(EBadTarget, nil)) // coldboot race window similar to an unexpected KOBJ_MOVE, but does // not result in inconsistent state if dropped return } if o.State != StateBound { - s.reportErr(UnbindStateError(o.Clone())) + s.reportErr(e.NewError(EUnbindState, o)) } o.State = StateNew o.Driver = "" @@ -423,7 +441,7 @@ func (s *State) processEvent(e *Event) { } } -// BadSequenceError is reported by [State.Consume] for an unexpected SEQNUM. +// BadSequenceError is reported for an unexpected SEQNUM. type BadSequenceError struct{ Got, Want uint64 } func (e BadSequenceError) Error() string { |
