diff options
Diffstat (limited to 'cmd/earlyinit/main.go')
| -rw-r--r-- | cmd/earlyinit/main.go | 149 |
1 files changed, 129 insertions, 20 deletions
diff --git a/cmd/earlyinit/main.go b/cmd/earlyinit/main.go index 0e17d78c..df454e3d 100644 --- a/cmd/earlyinit/main.go +++ b/cmd/earlyinit/main.go @@ -5,17 +5,88 @@ package main import ( + "context" + "crypto/rand" "log" "os" + "os/signal" "runtime" + "runtime/pprof" + "slices" "strings" . "syscall" + + "hakurei.app/internal/kobject" + "hakurei.app/internal/report" + "hakurei.app/internal/uevent" ) -func main() { - runtime.LockOSThread() +var r report.Reporter + +func init() { log.SetFlags(0) log.SetPrefix("earlyinit: ") + r.SetOutput(log.Default()) + + // this handles SIGQUIT to provide useful debugging information without + // terminating, and prevents the runtime from throwing on the must family + // of early error reporting functions, DO NOT REMOVE + c := make(chan os.Signal, 1) + signal.Notify(c, SIGQUIT) + go func() { + for { + <-c + if p := pprof.Lookup("goroutine"); p == nil { + log.Println("initial built-in goroutine profile does not exist") + } else if err := p.WriteTo(os.Stderr, 2); err != nil { + log.Println(err) + } + } + }() +} + +// fatal calls [log.Println] with v and blocks forever. Must be called from +// main. Must not be used after error reporting is set up. +func fatal(v ...any) { + log.Println(v...) + log.Println("unable to continue, please reboot and resolve the problem manually") + select {} +} + +// must calls fatal with err if it is non-nil. +func must(err error) { + if err != nil { + log.Println(err) + select {} + } +} + +// mustSyscall is like must, but with an additional action name. +func mustSyscall(action string, err error) { + if err != nil { + fatal("cannot "+action+":", err) + select {} + } +} + +// must1 is like must, but with an additional passed through value. +func must1[T any](v T, err error) T { + must(err) + return v +} + +const ( + // optionSystem specifies devpath of the system device. + optionSystem = "system" + + // flagStrict sets [report.DStrict] on r. + flagStrict = "strict" + // flagNoRecover sets [report.DNoRecover] on r. + flagNoRecover = "no_recover" +) + +func main() { + runtime.LockOSThread() var ( option map[string]string @@ -33,15 +104,33 @@ func main() { } } - if err := Mount( + { + var flag uint64 + if slices.Contains(flags, flagStrict) { + flag |= report.DStrict + } + if slices.Contains(flags, flagNoRecover) { + flag |= report.DNoRecover + } + log.Printf("reporting flags %x", flag) + r.SetFlags(flag) + } + + mustSyscall("mount devtmpfs", Mount( "devtmpfs", "/dev/", "devtmpfs", MS_NOSUID|MS_NOEXEC, "", - ); err != nil { - log.Fatalf("cannot mount devtmpfs: %v", err) - } + )) + must(os.Mkdir("/dev/pts/", 0)) + mustSyscall("mount devpts", Mount( + "devpts", + "/dev/pts/", + "devpts", + MS_NOSUID|MS_NOEXEC, + "mode=620,ptmxmode=666", + )) // The kernel might be unable to set up the console. When that happens, // printk is called with "Warning: unable to open an initial console." @@ -98,6 +187,40 @@ func main() { "", )) + conn := must1(uevent.Dial(-128 * 1024 * 1024)) + events := make(chan *uevent.Message, 1<<10) + var uuid uevent.UUID + must1(rand.Read(uuid[:])) + ctx, cancel := context.WithCancel(context.Background()) + + go consume(ctx, &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) { + r.Dispatch( + report.Inconsistent, + "processed inconsistent uevent", + err, + ) + }) + go func() { + s.Consume(ctx, events) + + log.Println("closing NETLINK_KOBJECT_UEVENT socket") + cancel() + if err := conn.Close(); err != nil { + log.Fatal(err) // not reached + } + }() + + must(os.Mkdir("/system", 0)) + if devpath := option[optionSystem]; devpath == "" { + fatal("system must be nonempty") + } else { + log.Printf("waiting for devpath pattern %q", devpath) + mustMountSystem(ctx, s, devpath) + } + // after top level has been set up mustSyscall("remount root", Mount( "", @@ -115,17 +238,3 @@ func main() { )) } - -// mustSyscall calls [log.Fatalln] if err is non-nil. -func mustSyscall(action string, err error) { - if err != nil { - log.Fatalln("cannot "+action+":", err) - } -} - -// must calls [log.Fatal] with err if it is non-nil. -func must(err error) { - if err != nil { - log.Fatal(err) - } -} |
