diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-06-16 23:26:55 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-06-16 23:27:50 +0900 |
| commit | f46a0370a723f3920fc3be5edc8ca630d9039dc5 (patch) | |
| tree | a9bf694eb563211f1ca76550279baa3d6dbadad5 /cmd/app | |
| parent | 5ee66a86eea424f05ee88baba3c5c81d820555c7 (diff) | |
cmd/app: initial container template command
This program is an experimental frontend for cmd/hakurei. This serves as a short-term replacement of the nixos module and a testing environment to implement API useful to planterette.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'cmd/app')
| -rw-r--r-- | cmd/app/main.go | 140 | ||||
| -rw-r--r-- | cmd/app/run.go | 51 |
2 files changed, 191 insertions, 0 deletions
diff --git a/cmd/app/main.go b/cmd/app/main.go new file mode 100644 index 00000000..a10412e9 --- /dev/null +++ b/cmd/app/main.go @@ -0,0 +1,140 @@ +// 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" + "log" + "os" + "os/signal" + "path/filepath" + "syscall" + + "hakurei.app/check" + "hakurei.app/command" + "hakurei.app/fhs" + "hakurei.app/hst" + "hakurei.app/message" +) + +func main() { + log.SetFlags(0) + log.SetPrefix("app: ") + msg := message.New(log.Default()) + + ctx, stop := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP) + defer stop() + + var ( + flagVerbose bool + flagBase string + + base, template, initial, upper, work *check.Absolute + ) + c := command.New(os.Stderr, log.Printf, "app", func([]string) (err error) { + msg.SwapVerbose(flagVerbose) + flagBase = os.ExpandEnv(flagBase) + if flagBase == "" { + flagBase = "state" + } + if flagBase, err = filepath.Abs(flagBase); err != nil { + return + } else if base, err = check.NewAbs(flagBase); err != nil { + return + } + + template = base.Append("template") + initial = template.Append("initial") + upper = template.Append("upper") + work = template.Append("work") + return + }).Flag( + &flagVerbose, + "v", command.BoolFlag(false), + "Increase log verbosity", + ).Flag( + &flagBase, + "d", command.StringFlag("$HAKUREI_APP_PATH"), + "Configuration and state directory", + ) + + { + var ( + flagShell string + flagHome string + ) + c.NewCommand( + "enter", "Enter mutable state template", + func([]string) error { + config := hst.Config{ + ID: "app.hakurei.mutable", + Container: &hst.ContainerConfig{ + Hostname: "mutable", + Filesystem: []hst.FilesystemConfigJSON{ + {FilesystemConfig: &hst.FSOverlay{ + Target: fhs.AbsRoot, + Lower: []*check.Absolute{initial}, + Upper: upper, + Work: work, + }}, + {FilesystemConfig: &hst.FSEphemeral{ + Target: fhs.AbsTmp, + Write: true, + Perm: 0755, + }}, + }, + Username: "chronos", + Flags: hst.FMultiarch | + hst.FDevel | + hst.FUserns | + hst.FHostNet | + hst.FTty, + }, + } + + if a, err := check.NewAbs(flagShell); err != nil { + return err + } else { + config.Container.Shell = a + config.Container.Path = a + config.Container.Args = []string{ + "-" + filepath.Base(flagShell), + } + } + + if a, err := check.NewAbs(flagHome); err != nil { + return err + } else { + config.Container.Home = a + } + + return run(ctx, msg, &config) + }, + ).Flag( + &flagShell, + "shell", command.StringFlag("/bin/zsh"), + "Shell program within container", + ).Flag( + &flagHome, + "home", command.StringFlag("/home/chronos"), + "Home directory within container", + ) + } + + c.MustParse(os.Args[1:], func(err error) { + 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) + } + } + }) +} diff --git a/cmd/app/run.go b/cmd/app/run.go new file mode 100644 index 00000000..16f8b885 --- /dev/null +++ b/cmd/app/run.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "encoding/json" + "os" + "os/exec" + "syscall" + + "hakurei.app/hst" + "hakurei.app/message" +) + +// run starts a container via cmd/hakurei and returns after it terminates. +func run(ctx context.Context, msg message.Msg, config *hst.Config) error { + c, cancel := context.WithCancel(ctx) + defer cancel() + + cmd := exec.CommandContext(c, "hakurei") + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + cmd.Cancel = func() error { + return cmd.Process.Signal(syscall.SIGINT) + } + if msg.IsVerbose() { + cmd.Args = append(cmd.Args, "-v") + } + cmd.Args = append(cmd.Args, "run", "3") + + r, w, err := os.Pipe() + if err != nil { + return err + } + cmd.ExtraFiles = append(cmd.ExtraFiles, r) + + if err = cmd.Start(); err != nil { + _, _ = r.Close(), w.Close() + return err + } + + if err = r.Close(); err != nil { + _ = w.Close() + return err + } else if err = json.NewEncoder(w).Encode(&config); err != nil { + _ = w.Close() + return err + } else if err = w.Close(); err != nil { + return err + } + + return cmd.Wait() +} |
