diff options
Diffstat (limited to 'cmd/app/main.go')
| -rw-r--r-- | cmd/app/main.go | 137 |
1 files changed, 119 insertions, 18 deletions
diff --git a/cmd/app/main.go b/cmd/app/main.go index 00bceb57..9b51d601 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -1,24 +1,25 @@ -// The app program is a proof-of-concept frontend for cmd/hakurei. -// -// This program is not covered by the compatibility promise. The command line -// interface and configuration syntax may change at any time. package main import ( "context" "errors" + "fmt" "io" "log" "os" "os/exec" "os/signal" "path/filepath" + "slices" + "strconv" + "strings" "syscall" "hakurei.app/check" "hakurei.app/command" "hakurei.app/fhs" "hakurei.app/hst" + "hakurei.app/internal/outcome" "hakurei.app/message" ) @@ -36,7 +37,7 @@ func main() { flagBase string flagInsecure bool - base, template, initial *check.Absolute + b *base ) c := command.New(os.Stderr, log.Printf, "app", func([]string) (err error) { msg.SwapVerbose(flagVerbose) @@ -44,14 +45,15 @@ func main() { if flagBase == "" { flagBase = "state" } + + var a *check.Absolute if flagBase, err = filepath.Abs(flagBase); err != nil { return - } else if base, err = check.NewAbs(flagBase); err != nil { + } else if a, err = check.NewAbs(flagBase); err != nil { return } + b = (*base)(a) - template = base.Append("template") - initial = base.Append("initial") return }).Flag( &flagVerbose, @@ -76,7 +78,7 @@ func main() { "enter", "Enter mutable state template", func(args []string) error { if len(args) != 1 { - return list(template, true) + return list(b.append("template"), true) } config := hst.Config{ @@ -86,9 +88,9 @@ func main() { Filesystem: []hst.FilesystemConfigJSON{ {FilesystemConfig: &hst.FSOverlay{ Target: fhs.AbsRoot, - Lower: []*check.Absolute{initial}, - Upper: template.Append(args[0]), - Work: base.Append("work", args[0]), + Lower: []*check.Absolute{b.initial()}, + Upper: b.template(args[0]), + Work: b.work(args[0]), }}, {FilesystemConfig: &hst.FSEphemeral{ Target: fhs.AbsTmp, @@ -122,7 +124,7 @@ func main() { config.Container.Home = a } - remove, err := acquireTemplate(base, args[0]) + remove, err := acquireTemplate(b, args[0]) if err != nil { return err } @@ -148,19 +150,19 @@ func main() { "run", "Start the named application", func(args []string) error { if len(args) < 1 { - return list(base.Append("app"), false) + return list(b.append("app"), false) } var config *hst.Config var r io.Reader - f, err := os.Open(base.Append("app", args[0]).String()) + f, err := os.Open(b.append("app", args[0]).String()) if err != nil { return err } r = f var common *os.File - if common, err = os.Open(base.Append("common").String()); err != nil { + if common, err = os.Open(b.append("common").String()); err != nil { if !errors.Is(err, os.ErrNotExist) { _ = f.Close() return err @@ -170,7 +172,7 @@ func main() { } var name string - config, err = parse(args[0], base, r, &name) + config, err = parse(args[0], b, r, &name) if closeErr := f.Close(); err == nil { err = closeErr } @@ -187,7 +189,7 @@ func main() { config.Container.Args[2] = flagCommand } - if err = enterTemplate(base, name); err != nil { + if err = enterTemplate(b, name); err != nil { return err } return run(ctx, msg, flagInsecure, config, args[1:]...) @@ -200,6 +202,105 @@ func main() { ) } + c.NewCommand( + "id", "Show user/group id of the specified appid", + func(args []string) error { + var appid int + switch len(args) { + case 0: + log.Println("appid not specified, assuming reserved user") + break + + case 1: + var err error + appid, err = strconv.Atoi(args[0]) + if err != nil { + return os.ErrInvalid + } + break + + default: + return errors.New("id requires 1 argument") + } + + fmt.Println(hst.ToUser(outcome.Info().User, appid)) + return nil + }, + ) + + c.NewCommand( + "next", "Find next unused identity", + func([]string) error { + var names []string + if dents, err := os.ReadDir(b.append("app").String()); err != nil { + return err + } else { + names = make([]string, 0, len(dents)) + for _, dent := range dents { + name := dent.Name() + if dent.IsDir() || (len(name) > 0 && name[0] == '.') { + continue + } + names = append(names, name) + } + } + + apps := make([]*hst.Config, len(names)) + for i, name := range names { + r, err := os.Open(b.append("app", name).String()) + if err != nil { + return err + } + + apps[i], err = parse(name, b, r, nil) + if closeErr := r.Close(); err == nil { + err = closeErr + } + if err != nil { + return err + } + } + + p := make(map[int][]*hst.Config) + for _, config := range apps { + p[config.Identity] = append(p[config.Identity], config) + } + + identities := make([]int, 0, len(p)) + for identity, a := range p { + identities = append(identities, identity) + if msg.IsVerbose() && len(a) != 1 { + ids := make([]string, len(a)) + for i, config := range a { + ids[i] = config.ID + } + slices.Sort(ids) + msg.Verbosef( + "%s shares identity %d", + strings.Join(ids, ", "), identity, + ) + } + } + + if len(identities) == 0 { + // 0 is the reserved identity + fmt.Println(1) + return nil + } + + slices.Sort(identities) + next := identities[0] - 1 + for _, identity := range identities { + if identity != next+1 { + break + } + next = identity + } + fmt.Println(next + 1) + return nil + }, + ) + c.MustParse(os.Args[1:], func(err error) { if e, ok := errors.AsType[*exec.ExitError](err); ok && e != nil { os.Exit(e.ExitCode()) |
