package main import ( "bufio" "fmt" "io" "strconv" "strings" "hakurei.app/check" "hakurei.app/fhs" "hakurei.app/hst" ) // parsePair parses a NUL-delimited quoted paths pair. func parsePair(s string) (source, target *check.Absolute, err error) { var p string if p, err = strconv.Unquote(s); err != nil { return } _source, _target, ok := strings.Cut(p, "\x00") if source, err = check.NewAbs(_source); err != nil { return } if !ok { return } target, err = check.NewAbs(_target) return } // parse decodes a high-level configuration stream and returns its // corresponding [hst.Config]. func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) { shell := fhs.AbsRoot.Append("bin", "zsh") home := hst.AbsPrivateTmp.Append("home") c := hst.Config{ ID: id, Enablements: new(hst.Enablements), SessionBus: &hst.BusConfig{ Own: []string{ id + ".*", "org.mpris.MediaPlayer2." + id + ".*", }, Filter: true, }, SystemBus: &hst.BusConfig{Filter: true}, Container: &hst.ContainerConfig{ Env: make(map[string]string), Filesystem: []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSOverlay{ Target: fhs.AbsRoot, Lower: []*check.Absolute{ base.Append("template", "initial"), }, Upper: base.Append("template", "upper"), }}, {FilesystemConfig: &hst.FSBind{ Target: home, Source: base.Append("state", id), Write: true, Ensure: true, }}, {FilesystemConfig: &hst.FSEphemeral{ Target: fhs.AbsVar.Append("tmp"), Write: true, Perm: 01777, }}, {FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("block")}}, {FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("bus")}}, {FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("class")}}, {FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("dev")}}, {FilesystemConfig: &hst.FSBind{Source: fhs.AbsSys.Append("devices")}}, }, Username: "chronos", Shell: shell, Home: home, Path: shell, Args: []string{"zsh", "-c"}, Flags: hst.FCoverRun, }, } s := bufio.NewScanner(r) scanOnce := func() error { if s.Scan() { return nil } if err := s.Err(); err != nil { return err } return io.ErrUnexpectedEOF } if err := scanOnce(); err != nil { return nil, err } if v, err := strconv.Atoi(s.Text()); err != nil { return nil, err } else { c.Identity = v } if err := scanOnce(); err != nil { return nil, err } c.Container.Args = append(c.Container.Args, s.Text()) var flagGPU, flagSystemBus bool flags := map[string]*bool{ "gpu": &flagGPU, "system_bus": &flagSystemBus, } for s.Scan() { key, value, ok := strings.Cut(s.Text(), " ") if key != "" && key[0] == ';' { continue } if !ok { if key == "" { continue } var p *bool if p, ok = flags[key]; ok { *p = true continue } switch key { case "wayland": *c.Enablements |= hst.EWayland case "x11": *c.Enablements |= hst.EX11 case "dbus": *c.Enablements |= hst.EDBus case "pipewire": *c.Enablements |= hst.EPipeWire case "multiarch": c.Container.Flags |= hst.FMultiarch case "devel": c.Container.Flags |= hst.FDevel case "userns": c.Container.Flags |= hst.FUserns case "net": c.Container.Flags |= hst.FHostNet case "abstract": c.Container.Flags |= hst.FHostAbstract case "tty": c.Container.Flags |= hst.FTty case "mapuid": c.Container.Flags |= hst.FMapRealUID case "device": c.Container.Flags |= hst.FDevice case "share_runtime": c.Container.Flags |= hst.FShareRuntime case "share_tmpdir": c.Container.Flags |= hst.FShareTmpdir default: return nil, fmt.Errorf("invalid flag %q", key) } continue } switch key { case "group": c.Groups = append(c.Groups, value) continue case "env": if key, value, ok = strings.Cut(value, "="); !ok { return nil, fmt.Errorf("invalid environment %q", key) } c.Container.Env[key] = value continue case "ro": source, target, err := parsePair(value) if err != nil { return nil, err } c.Container.Filesystem = append(c.Container.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{ Target: target, Source: source, }}, ) continue case "rw": source, target, err := parsePair(value) if err != nil { return nil, err } c.Container.Filesystem = append(c.Container.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSBind{ Target: target, Source: source, Write: true, }}, ) continue case "own": c.SessionBus.Own = append(c.SessionBus.Own, value) continue case "own_system": c.SystemBus.Own = append(c.SystemBus.Own, value) continue case "talk": c.SessionBus.Talk = append(c.SessionBus.Talk, value) continue case "talk_system": c.SystemBus.Talk = append(c.SystemBus.Talk, value) continue default: return nil, fmt.Errorf("invalid key %q", key) } } if err := s.Err(); err != nil { return nil, err } if flagGPU { c.Container.Filesystem = append(c.Container.Filesystem, []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSBind{ Source: fhs.AbsDev.Append("dri"), Device: true, Optional: true, }}, }...) } if !flagSystemBus { c.SystemBus = nil } if c.Container.Flags&hst.FShareTmpdir == 0 { c.Container.Filesystem = append(c.Container.Filesystem, hst.FilesystemConfigJSON{FilesystemConfig: &hst.FSEphemeral{ Target: fhs.AbsTmp, Write: true, Perm: 01777, }}, ) } return &c, nil }