From b18f40d974ffc33f3703d46bfe42d6fa2f844a01 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Wed, 27 May 2026 17:58:01 +0900 Subject: [PATCH] internal/kobject: pass action kind for range Useful for handling most uevents. Signed-off-by: Ophestra --- cmd/earlyinit/main.go | 8 +++++++- cmd/earlyinit/mount.go | 6 ++++-- cmd/earlyinit/uevent.go | 5 +++-- internal/kobject/kobject.go | 33 +++++++++++++++++--------------- internal/kobject/kobject_test.go | 12 +++++++++--- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/cmd/earlyinit/main.go b/cmd/earlyinit/main.go index df454e3d..b5e8bcde 100644 --- a/cmd/earlyinit/main.go +++ b/cmd/earlyinit/main.go @@ -19,6 +19,7 @@ import ( "hakurei.app/internal/kobject" "hakurei.app/internal/report" "hakurei.app/internal/uevent" + "hakurei.app/message" ) var r report.Reporter @@ -79,6 +80,8 @@ const ( // optionSystem specifies devpath of the system device. optionSystem = "system" + // flagVerbose increases output verbosity. + flagVerbose = "verbose" // flagStrict sets [report.DStrict] on r. flagStrict = "strict" // flagNoRecover sets [report.DNoRecover] on r. @@ -116,6 +119,9 @@ func main() { r.SetFlags(flag) } + msg := message.New(log.Default()) + msg.SwapVerbose(slices.Contains(flags, flagVerbose)) + mustSyscall("mount devtmpfs", Mount( "devtmpfs", "/dev/", @@ -193,7 +199,7 @@ func main() { must1(rand.Read(uuid[:])) ctx, cancel := context.WithCancel(context.Background()) - go consume(ctx, &r, conn, uuid, events) + go consume(ctx, msg, &r, conn, uuid, events) s := kobject.New(uuid, func(o *kobject.Object, env map[string]string) { log.Printf("change %s: %q", o.DevPath, env) }, func(err error) { diff --git a/cmd/earlyinit/mount.go b/cmd/earlyinit/mount.go index 6ac59a46..51d4ecc9 100644 --- a/cmd/earlyinit/mount.go +++ b/cmd/earlyinit/mount.go @@ -12,6 +12,7 @@ import ( "hakurei.app/check" "hakurei.app/fhs" "hakurei.app/internal/kobject" + "hakurei.app/internal/uevent" ) // mustMountSystem waits for and mounts a system device matching pattern. @@ -26,8 +27,9 @@ func mustMountSystem( for { var matchErr error var systemPath *check.Absolute - s.Range(c, func(o *kobject.Object) bool { - if o.Subsystem != "block" || + s.Range(c, func(o *kobject.Object, act uevent.KobjectAction) bool { + if (act != uevent.KOBJ_ADD && act != uevent.KOBJ_CHANGE) || + o.Subsystem != "block" || o.Env["DEVTYPE"] != "disk" { return true } diff --git a/cmd/earlyinit/uevent.go b/cmd/earlyinit/uevent.go index 0ab134cf..64f548ba 100644 --- a/cmd/earlyinit/uevent.go +++ b/cmd/earlyinit/uevent.go @@ -2,12 +2,12 @@ package main import ( "context" - "log" "time" "hakurei.app/fhs" "hakurei.app/internal/report" "hakurei.app/internal/uevent" + "hakurei.app/message" ) // newRejectColdboot returns a function to be called on every subsequent pending @@ -56,6 +56,7 @@ func newRejectColdboot() func() bool { // consume continuously consumes events from conn with retries. func consume( ctx context.Context, + msg message.Msg, r *report.Reporter, conn *uevent.Conn, uuid uevent.UUID, @@ -67,7 +68,7 @@ func consume( coldboot := true retry: if dispatchErr := conn.Consume(ctx, fhs.Sys, &uuid, events, coldboot, func(path string) { - log.Println("coldboot visited", path) + msg.Verbose("coldboot visited", path) }, func(err error) bool { if _, ok := err.(uevent.NeedsColdboot); ok && !nextColdboot() { r.Dispatch( diff --git a/internal/kobject/kobject.go b/internal/kobject/kobject.go index 26359114..2acf3de0 100644 --- a/internal/kobject/kobject.go +++ b/internal/kobject/kobject.go @@ -101,7 +101,7 @@ func (o *Object) update(env map[string]string, strip bool) { // A pendingIterator is a callback currently iterating through objects targeted // by ongoing events. type pendingIterator struct { - f func(o *Object) bool + f func(o *Object, act uevent.KobjectAction) bool done chan<- struct{} } @@ -150,12 +150,12 @@ func (s *State) deleteIter(p *pendingIterator) { } // dispatchIter broadcasts an [Object] to all alive iterators. -func (s *State) dispatchIter(o *Object) { +func (s *State) dispatchIter(o *Object, act uevent.KobjectAction) { s.iterMu.Lock() defer s.iterMu.Unlock() for _, p := range s.iter { - if !p.f(o) { + if !p.f(o, act) { s.deleteIter(p) close(p.done) } @@ -165,14 +165,17 @@ func (s *State) dispatchIter(o *Object) { // Range calls f on all current and upcoming [Object] values tracked by s until // f returns false or the context is cancelled. f must not retain o or modify // the value it points to. -func (s *State) Range(ctx context.Context, f func(o *Object) bool) { +func (s *State) Range( + ctx context.Context, + f func(o *Object, act uevent.KobjectAction) bool, +) { done := make(chan struct{}) p := pendingIterator{f, done} s.iterMu.Lock() s.ueventMu.RLock() for _, o := range s.uevent { - if !f(o) { + if !f(o, uevent.KOBJ_ADD) { s.ueventMu.RUnlock() s.iterMu.Unlock() return @@ -296,13 +299,13 @@ func (s *State) processEvent(e *Event) { return } - switch e.Action { + switch act := e.Action; act { case uevent.KOBJ_ADD: if e.Synth == nil { if o, ok := s.uevent[e.DevPath]; ok { s.reportErr(e.NewError(EDuplicateAdd, o)) o.merge(e.Env) - s.dispatchIter(o) + s.dispatchIter(o, act) return } } @@ -312,7 +315,7 @@ func (s *State) processEvent(e *Event) { } o.merge(e.Env) s.uevent[e.DevPath] = o - s.dispatchIter(o) + s.dispatchIter(o, act) return case uevent.KOBJ_REMOVE: @@ -338,14 +341,14 @@ func (s *State) processEvent(e *Event) { o = e.makeColdboot() o.merge(e.Env) s.uevent[e.DevPath] = o - s.dispatchIter(o) + s.dispatchIter(o, act) return } o.update(e.Env, true) if s.handleChange != nil { s.handleChange(o, e.Env) } - s.dispatchIter(o) + s.dispatchIter(o, act) return case uevent.KOBJ_MOVE: @@ -368,7 +371,7 @@ func (s *State) processEvent(e *Event) { o.merge(e.Env) s.uevent[e.DevPath] = o o.DevPath = e.DevPath - s.dispatchIter(o) + s.dispatchIter(o, act) return case uevent.KOBJ_ONLINE: @@ -384,7 +387,7 @@ func (s *State) processEvent(e *Event) { s.reportErr(e.NewError(EUnexpectedOffline, o)) } o.Offline = false - s.dispatchIter(o) + s.dispatchIter(o, act) return case uevent.KOBJ_OFFLINE: @@ -400,7 +403,7 @@ func (s *State) processEvent(e *Event) { s.reportErr(e.NewError(EUnexpectedOffline, o)) } o.Offline = true - s.dispatchIter(o) + s.dispatchIter(o, act) return case uevent.KOBJ_BIND: @@ -416,7 +419,7 @@ func (s *State) processEvent(e *Event) { } o.State = StateBound o.merge(e.Env) - s.dispatchIter(o) + s.dispatchIter(o, act) return case uevent.KOBJ_UNBIND: @@ -432,7 +435,7 @@ func (s *State) processEvent(e *Event) { } o.State = StateNew o.Driver = "" - s.dispatchIter(o) + s.dispatchIter(o, act) return default: // not reached diff --git a/internal/kobject/kobject_test.go b/internal/kobject/kobject_test.go index b184e621..c6a54904 100644 --- a/internal/kobject/kobject_test.go +++ b/internal/kobject/kobject_test.go @@ -388,7 +388,9 @@ func TestIter(t *testing.T) { "SEQNUM=1", }} synctest.Wait() - s.Range(t.Context(), func(o *Object) bool { return false }) + s.Range(t.Context(), func(*Object, uevent.KobjectAction) bool { + return false + }) var got []*Object check := func(want []*Object) { @@ -405,7 +407,7 @@ func TestIter(t *testing.T) { defer cancel() var done bool wg.Go(func() { - s.Range(ctx, func(o *Object) bool { + s.Range(ctx, func(o *Object, _ uevent.KobjectAction) bool { got = append(got, o.Clone()) return !done }) @@ -437,7 +439,11 @@ func TestIter(t *testing.T) { }, }) - wg.Go(func() { s.Range(ctx, func(*Object) bool { return true }) }) + wg.Go(func() { + s.Range(ctx, func(*Object, uevent.KobjectAction) bool { + return true + }) + }) synctest.Wait() iter := reflect.ValueOf(s).Elem().FieldByName("iter")