// The mbf program is a frontend for [hakurei.app/internal/rosa]. // // This program is not covered by the compatibility promise. The command line // interface, available packages and their behaviour, and even the on-disk // format, may change at any time. // // # Name // // The name mbf stands for maiden's best friend, as a tribute to the DOOM source // port of [the same name]. This name is a placeholder and is subject to change. // // [the same name]: https://www.doomwiki.org/wiki/MBF package main import ( "context" "crypto/sha512" "errors" "fmt" "io" "log" "net" "os" "os/signal" "path/filepath" "runtime" "strconv" "sync" "sync/atomic" "syscall" "time" "unique" "hakurei.app/check" "hakurei.app/command" "hakurei.app/container" "hakurei.app/container/seccomp" "hakurei.app/container/std" "hakurei.app/ext" "hakurei.app/fhs" "hakurei.app/internal/pkg" "hakurei.app/internal/rosa" "hakurei.app/message" ) func main() { container.TryArgv0(nil) log.SetFlags(0) log.SetPrefix("mbf: ") msg := message.New(log.Default()) if os.Geteuid() == 0 { log.Fatal("this program must not run as root") } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) defer stop() var cm cache defer func() { cm.Close() if r := recover(); r != nil { fmt.Println(r) log.Fatal("consider scrubbing the on-disk cache") } }() var ( flagQuiet bool addr net.UnixAddr ) c := command.New(os.Stderr, log.Printf, "mbf", func([]string) error { msg.SwapVerbose(!flagQuiet) cm.ctx, cm.msg = ctx, msg cm.base = os.ExpandEnv(cm.base) addr.Net = "unix" addr.Name = os.ExpandEnv(addr.Name) if addr.Name == "" { addr.Name = "daemon" } return nil }).Flag( &flagQuiet, "q", command.BoolFlag(false), "Do not print cure messages", ).Flag( &cm.cures, "cures", command.IntFlag(0), "Maximum number of dependencies to cure at any given time", ).Flag( &cm.jobs, "jobs", command.IntFlag(0), "Preferred number of jobs to run, when applicable", ).Flag( &cm.base, "d", command.StringFlag("$MBF_CACHE_DIR"), "Directory to store cured artifacts", ).Flag( &cm.idle, "sched-idle", command.BoolFlag(false), "Set SCHED_IDLE scheduling policy", ).Flag( &cm.hostAbstract, "host-abstract", command.BoolFlag( os.Getenv("MBF_HOST_ABSTRACT") != "", ), "Do not restrict networked cure containers from connecting to host "+ "abstract UNIX sockets", ).Flag( &addr.Name, "socket", command.StringFlag("$MBF_DAEMON_SOCKET"), "Pathname of socket to bind to", ) c.NewCommand( "checksum", "Compute checksum of data read from standard input", func([]string) error { go func() { <-ctx.Done(); os.Exit(1) }() h := sha512.New384() if _, err := io.Copy(h, os.Stdin); err != nil { return err } log.Println(pkg.Encode(pkg.Checksum(h.Sum(nil)))) return nil }, ) { var flagShifts int c.NewCommand( "scrub", "Examine the on-disk cache for errors", func(args []string) error { if len(args) > 0 { return errors.New("scrub expects no arguments") } if flagShifts < 0 || flagShifts > 31 { flagShifts = 12 } return cm.Do(func(cache *pkg.Cache) error { return cache.Scrub(runtime.NumCPU() << flagShifts) }) }, ).Flag( &flagShifts, "shift", command.IntFlag(12), "Scrub parallelism size exponent, to the power of 2", ) } { var ( flagStatus bool flagReport string ) c.NewCommand( "info", "Display out-of-band metadata of an artifact", func(args []string) (err error) { return commandInfo(&cm, args, os.Stdout, flagStatus, flagReport) }, ).Flag( &flagStatus, "status", command.BoolFlag(false), "Display cure status if available", ).Flag( &flagReport, "report", command.StringFlag(""), "Load cure status from this report file instead of cache", ) } c.NewCommand( "report", "Generate an artifact cure report for the current cache", func(args []string) (err error) { var w *os.File switch len(args) { case 0: w = os.Stdout case 1: if w, err = os.OpenFile( args[0], os.O_CREATE|os.O_EXCL|syscall.O_WRONLY, 0400, ); err != nil { return } defer func() { closeErr := w.Close() if err == nil { err = closeErr } }() default: return errors.New("report requires 1 argument") } if ext.Isatty(int(w.Fd())) { return errors.New("output appears to be a terminal") } return cm.Do(func(cache *pkg.Cache) error { return rosa.WriteReport(msg, w, cache) }) }, ) { var flagJobs int c.NewCommand("updates", command.UsageInternal, func([]string) error { var ( errsMu sync.Mutex errs []error n atomic.Uint64 ) w := make(chan rosa.PArtifact) var wg sync.WaitGroup for range max(flagJobs, 1) { wg.Go(func() { for p := range w { meta := rosa.GetMetadata(p) if meta.ID == 0 { continue } v, err := meta.GetVersions(ctx) if err != nil { errsMu.Lock() errs = append(errs, err) errsMu.Unlock() continue } if current, latest := rosa.Std.Version(p), meta.GetLatest(v); current != latest { n.Add(1) log.Printf("%s %s < %s", meta.Name, current, latest) continue } msg.Verbosef("%s is up to date", meta.Name) } }) } done: for i := range rosa.PresetEnd { select { case w <- rosa.PArtifact(i): break case <-ctx.Done(): break done } } close(w) wg.Wait() if v := n.Load(); v > 0 { errs = append(errs, errors.New(strconv.Itoa(int(v))+ " package(s) are out of date")) } return errors.Join(errs...) }).Flag( &flagJobs, "j", command.IntFlag(32), "Maximum number of simultaneous connections", ) } c.NewCommand( "daemon", "Service artifact IR with Rosa OS extensions", func(args []string) error { ul, err := net.ListenUnix("unix", &addr) if err != nil { return err } log.Printf("listening on pathname socket at %s", addr.Name) return serve(ctx, log.Default(), &cm, ul) }, ) { var ( flagGentoo string flagChecksum string flagStage0 bool ) c.NewCommand( "stage3", "Check for toolchain 3-stage non-determinism", func(args []string) (err error) { t := rosa.Std if flagGentoo != "" { t -= 3 // magic number to discourage misuse var checksum pkg.Checksum if len(flagChecksum) != 0 { if err = pkg.Decode(&checksum, flagChecksum); err != nil { return } } rosa.SetGentooStage3(flagGentoo, checksum) } var ( pathname *check.Absolute checksum [2]unique.Handle[pkg.Checksum] ) if err = cm.Do(func(cache *pkg.Cache) (err error) { pathname, _, err = cache.Cure( (t - 2).Load(rosa.Clang), ) return }); err != nil { return } log.Println("stage1:", pathname) if err = cm.Do(func(cache *pkg.Cache) (err error) { pathname, checksum[0], err = cache.Cure( (t - 1).Load(rosa.Clang), ) return }); err != nil { return } log.Println("stage2:", pathname) if err = cm.Do(func(cache *pkg.Cache) (err error) { pathname, checksum[1], err = cache.Cure( t.Load(rosa.Clang), ) return }); err != nil { return } log.Println("stage3:", pathname) if checksum[0] != checksum[1] { err = &pkg.ChecksumMismatchError{ Got: checksum[0].Value(), Want: checksum[1].Value(), } } else { log.Println( "stage2 is identical to stage3", "("+pkg.Encode(checksum[0].Value())+")", ) } if flagStage0 { if err = cm.Do(func(cache *pkg.Cache) (err error) { pathname, _, err = cache.Cure( t.Load(rosa.Stage0), ) return }); err != nil { return } log.Println(pathname) } return }, ).Flag( &flagGentoo, "gentoo", command.StringFlag(""), "Bootstrap from a Gentoo stage3 tarball", ).Flag( &flagChecksum, "checksum", command.StringFlag(""), "Checksum of Gentoo stage3 tarball", ).Flag( &flagStage0, "stage0", command.BoolFlag(false), "Create bootstrap stage0 tarball", ) } { var ( flagDump string flagEnter bool flagExport string flagRemote bool ) c.NewCommand( "cure", "Cure the named artifact and show its path", func(args []string) error { if len(args) != 1 { return errors.New("cure requires 1 argument") } p, ok := rosa.ResolveName(args[0]) if !ok { return fmt.Errorf("unknown artifact %q", args[0]) } switch { default: var pathname *check.Absolute err := cm.Do(func(cache *pkg.Cache) (err error) { pathname, _, err = cache.Cure(rosa.Std.Load(p)) return }) if err != nil { return err } log.Println(pathname) if flagExport != "" { msg.Verbosef("exporting %s to %s...", args[0], flagExport) var f *os.File if f, err = os.OpenFile( flagExport, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400, ); err != nil { return err } else if _, err = pkg.Flatten( os.DirFS(pathname.String()), ".", f, ); err != nil { _ = f.Close() return err } else if err = f.Close(); err != nil { return err } } return nil case flagDump != "": f, err := os.OpenFile( flagDump, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644, ) if err != nil { return err } if err = pkg.NewIR().EncodeAll(f, rosa.Std.Load(p)); err != nil { _ = f.Close() return err } return f.Close() case flagEnter: return cm.Do(func(cache *pkg.Cache) error { return cache.EnterExec( ctx, rosa.Std.Load(p), true, os.Stdin, os.Stdout, os.Stderr, rosa.AbsSystem.Append("bin", "mksh"), "sh", ) }) case flagRemote: pathname, err := cureRemote(ctx, &addr, rosa.Std.Load(p)) if err == nil { log.Println(pathname) } return err } }, ).Flag( &flagDump, "dump", command.StringFlag(""), "Write IR to specified pathname and terminate", ).Flag( &flagExport, "export", command.StringFlag(""), "Export cured artifact to specified pathname", ).Flag( &flagEnter, "enter", command.BoolFlag(false), "Enter cure container with an interactive shell", ).Flag( &flagRemote, "daemon", command.BoolFlag(false), "Cure artifact on the daemon", ) } { var ( flagNet bool flagSession bool flagWithToolchain bool ) c.NewCommand( "shell", "Interactive shell in the specified Rosa OS environment", func(args []string) error { presets := make([]rosa.PArtifact, len(args)+3) for i, arg := range args { p, ok := rosa.ResolveName(arg) if !ok { return fmt.Errorf("unknown artifact %q", arg) } presets[i] = p } base := rosa.Clang if !flagWithToolchain { base = rosa.Musl } presets = append(presets, base, rosa.Mksh, rosa.Toybox, ) root := make(pkg.Collect, 0, 6+len(args)) root = rosa.Std.AppendPresets(root, presets...) if err := cm.Do(func(cache *pkg.Cache) error { _, _, err := cache.Cure(&root) return err }); err == nil { return errors.New("unreachable") } else if !pkg.IsCollected(err) { return err } type cureRes struct { pathname *check.Absolute checksum unique.Handle[pkg.Checksum] } cured := make(map[pkg.Artifact]cureRes) for _, a := range root { if err := cm.Do(func(cache *pkg.Cache) error { pathname, checksum, err := cache.Cure(a) if err == nil { cured[a] = cureRes{pathname, checksum} } return err }); err != nil { return err } } // explicitly open for direct error-free use from this point if cm.c == nil { if err := cm.open(); err != nil { return err } } layers := pkg.PromoteLayers(root, func(a pkg.Artifact) ( *check.Absolute, unique.Handle[pkg.Checksum], ) { res := cured[a] return res.pathname, res.checksum }, func(i int, d pkg.Artifact) { r := pkg.Encode(cm.c.Ident(d).Value()) if s, ok := d.(fmt.Stringer); ok { if name := s.String(); name != "" { r += "-" + name } } msg.Verbosef("promoted layer %d as %s", i, r) }) z := container.New(ctx, msg) z.WaitDelay = 3 * time.Second z.SeccompPresets = pkg.SeccompPresets z.SeccompFlags |= seccomp.AllowMultiarch z.ParentPerm = 0700 z.HostNet = flagNet z.RetainSession = flagSession z.Hostname = "localhost" z.Uid, z.Gid = (1<<10)-1, (1<<10)-1 z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr if s, ok := os.LookupEnv("TERM"); ok { z.Env = append(z.Env, "TERM="+s) } var tempdir *check.Absolute if s, err := filepath.Abs(os.TempDir()); err != nil { return err } else if tempdir, err = check.NewAbs(s); err != nil { return err } z.Dir = fhs.AbsRoot z.Env = []string{ "SHELL=/system/bin/mksh", "PATH=/system/bin", "HOME=/", } z.Path = rosa.AbsSystem.Append("bin", "mksh") z.Args = []string{"mksh"} z. OverlayEphemeral(fhs.AbsRoot, layers...). Place( fhs.AbsEtc.Append("hosts"), []byte("127.0.0.1 localhost\n"), ). Place( fhs.AbsEtc.Append("passwd"), []byte("media_rw:x:1023:1023::/:/system/bin/sh\n"+ "nobody:x:65534:65534::/proc/nonexistent:/system/bin/false\n"), ). Place( fhs.AbsEtc.Append("group"), []byte("media_rw:x:1023:\nnobody:x:65534:\n"), ). Bind(tempdir, fhs.AbsTmp, std.BindWritable). Proc(fhs.AbsProc).Dev(fhs.AbsDev, true) if err := z.Start(); err != nil { return err } if err := z.Serve(); err != nil { return err } return z.Wait() }, ).Flag( &flagNet, "net", command.BoolFlag(false), "Share host net namespace", ).Flag( &flagSession, "session", command.BoolFlag(true), "Retain session", ).Flag( &flagWithToolchain, "with-toolchain", command.BoolFlag(false), "Include the stage2 LLVM toolchain", ) } c.Command( "help", "Show this help message", func([]string) error { c.PrintHelp(); return nil }, ) c.MustParse(os.Args[1:], func(err error) { cm.Close() if w, ok := err.(interface{ Unwrap() []error }); !ok { log.Fatal(err) } else { errs := w.Unwrap() for i, e := range errs { if i == len(errs)-1 { log.Fatal(e) } log.Println(e) } } }) }