// The earlyinit is part of the Rosa OS initramfs and serves as the system init. // // This program is an internal detail of Rosa OS and is not usable on its own. // It is not covered by the compatibility promise. package main import ( "context" "crypto/rand" "log" "os" "os/exec" "os/signal" "runtime" "runtime/pprof" "slices" "strings" . "syscall" "hakurei.app/internal/kobject" "hakurei.app/internal/report" "hakurei.app/internal/uevent" ) 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 flags []string ) if len(os.Args) > 1 { option = make(map[string]string) for _, s := range os.Args[1:] { key, value, ok := strings.Cut(s, "=") if !ok { flags = append(flags, s) continue } option[key] = value } } { 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, "", )) 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." // and the init runs with no files. The checkfds runtime function // populates 0-2 by opening /dev/null for them. // // This check replaces 1 and 2 with /dev/kmsg to improve the chance // of output being visible to the user. if fi, err := os.Stdout.Stat(); err == nil { if stat, ok := fi.Sys().(*Stat_t); ok { if stat.Rdev == 0x103 { var fd int if fd, err = Open( "/dev/kmsg", O_WRONLY|O_CLOEXEC, 0, ); err != nil { log.Fatalf("cannot open kmsg: %v", err) } if err = Dup3(fd, Stdout, 0); err != nil { log.Fatalf("cannot open stdout: %v", err) } if err = Dup3(fd, Stderr, 0); err != nil { log.Fatalf("cannot open stderr: %v", err) } if err = Close(fd); err != nil { log.Printf("cannot close kmsg: %v", err) } } } } // staying in rootfs, these are no longer used must(os.Remove("/root")) must(os.Remove("/init")) must(os.Mkdir("/proc", 0)) mustSyscall("mount proc", Mount( "proc", "/proc", "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, "hidepid=1", )) must(os.Mkdir("/sys", 0)) mustSyscall("mount sysfs", Mount( "sysfs", "/sys", "sysfs", 0, "", )) 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( "", "/", "", MS_REMOUNT|MS_BIND| MS_RDONLY|MS_NODEV|MS_NOSUID|MS_NOEXEC, "", )) must(os.WriteFile( "/sys/module/firmware_class/parameters/path", []byte("/system/lib/firmware"), 0, )) }