aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd/earlyinit/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/earlyinit/main.go')
-rw-r--r--cmd/earlyinit/main.go149
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)
- }
-}