diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-05-26 19:16:32 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-05-27 10:37:45 +0900 |
| commit | 8c7109a2d58c40b86ad97ed520aff6b00bbf1667 (patch) | |
| tree | 547f611b77e98f5df433ba513cbcbc9dc47e4b8e /cmd/earlyinit/uevent.go | |
| parent | caf3e0db4b92d859599ee199b32051fae8c99016 (diff) | |
cmd/earlyinit: mount system device
This currently relies on a trusted bootloader to determine the boot device.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd/earlyinit/uevent.go')
| -rw-r--r-- | cmd/earlyinit/uevent.go | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/cmd/earlyinit/uevent.go b/cmd/earlyinit/uevent.go new file mode 100644 index 00000000..0ab134cf --- /dev/null +++ b/cmd/earlyinit/uevent.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "log" + "time" + + "hakurei.app/fhs" + "hakurei.app/internal/report" + "hakurei.app/internal/uevent" +) + +// newRejectColdboot returns a function to be called on every subsequent pending +// coldboot, and returns whether coldboot should proceed. Rejection is sticky. +func newRejectColdboot() func() bool { + // one coldboot per five minutes, two consecutive coldboot + const ( + coldbootInterval = 5 * time.Minute + coldbootBurst = 2 + ) + + done := make(chan struct{}) + s := make(chan struct{}, coldbootBurst) + s <- struct{}{} // for early fault before reporting is ready + go func() { + t := time.NewTicker(coldbootInterval) + for { + select { + case <-done: + return + + case <-t.C: + select { + case s <- struct{}{}: + default: + } + } + } + }() + + return func() bool { + select { + case <-s: + return true + + case <-done: + return false + + default: + close(done) + return false + } + } +} + +// consume continuously consumes events from conn with retries. +func consume( + ctx context.Context, + r *report.Reporter, + conn *uevent.Conn, + uuid uevent.UUID, + events chan<- *uevent.Message, +) { + defer close(events) + + nextColdboot := newRejectColdboot() + coldboot := true +retry: + if dispatchErr := conn.Consume(ctx, fhs.Sys, &uuid, events, coldboot, func(path string) { + log.Println("coldboot visited", path) + }, func(err error) bool { + if _, ok := err.(uevent.NeedsColdboot); ok && !nextColdboot() { + r.Dispatch( + report.Degraded, + "rejecting coldboot loop", + err, + ) + return false + } + r.Dispatch( + report.Inconsistent, + "consumed invalid message", + err, + ) + return true + }, nil); dispatchErr != nil { + if _, ok := dispatchErr.(uevent.Recoverable); !ok { + r.Dispatch( + report.Fatal, + "discontinuing uevent processing due to nonrecoverable error", + dispatchErr, + ) + return + } + + if _, ok := dispatchErr.(uevent.NeedsColdboot); ok { + // coldboot loop rejected by handler + coldboot = false + } + + goto retry + } +} |
