aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd/earlyinit/uevent.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-05-26 19:16:32 +0900
committerOphestra <cat@gensokyo.uk>2026-05-27 10:37:45 +0900
commit8c7109a2d58c40b86ad97ed520aff6b00bbf1667 (patch)
tree547f611b77e98f5df433ba513cbcbc9dc47e4b8e /cmd/earlyinit/uevent.go
parentcaf3e0db4b92d859599ee199b32051fae8c99016 (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.go103
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
+ }
+}