diff options
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/hakurei/command.go | 39 | ||||
| -rw-r--r-- | cmd/hakurei/command_test.go | 3 | ||||
| -rw-r--r-- | cmd/hakurei/main.go | 26 | ||||
| -rw-r--r-- | cmd/hakurei/parse.go | 24 | ||||
| -rw-r--r-- | cmd/hakurei/print.go | 5 | ||||
| -rw-r--r-- | cmd/hpkg/main.go | 37 | ||||
| -rw-r--r-- | cmd/hpkg/paths.go | 5 | ||||
| -rw-r--r-- | cmd/hpkg/proc.go | 13 | ||||
| -rw-r--r-- | cmd/hpkg/with.go | 17 |
9 files changed, 92 insertions, 77 deletions
diff --git a/cmd/hakurei/command.go b/cmd/hakurei/command.go index 1671902e..58e8ace1 100644 --- a/cmd/hakurei/command.go +++ b/cmd/hakurei/command.go @@ -17,17 +17,30 @@ import ( "hakurei.app/internal" "hakurei.app/internal/app" "hakurei.app/internal/app/state" - "hakurei.app/internal/hlog" "hakurei.app/system" "hakurei.app/system/dbus" ) -func buildCommand(ctx context.Context, out io.Writer) command.Command { +func buildCommand(ctx context.Context, msg container.Msg, early *earlyHardeningErrs, out io.Writer) command.Command { var ( flagVerbose bool flagJSON bool ) - c := command.New(out, log.Printf, "hakurei", func([]string) error { internal.InstallOutput(flagVerbose); return nil }). + c := command.New(out, log.Printf, "hakurei", func([]string) error { + msg.SwapVerbose(flagVerbose) + + if early.yamaLSM != nil { + msg.Verbosef("cannot enable ptrace protection via Yama LSM: %v", early.yamaLSM) + // not fatal + } + + if early.dumpable != nil { + log.Printf("cannot set SUID_DUMP_DISABLE: %s", early.dumpable) + // not fatal + } + + return nil + }). Flag(&flagVerbose, "v", command.BoolFlag(false), "Increase log verbosity"). Flag(&flagJSON, "json", command.BoolFlag(false), "Serialise output in JSON when applicable") @@ -39,10 +52,10 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command { } // config extraArgs... - config := tryPath(args[0]) + config := tryPath(msg, args[0]) config.Args = append(config.Args, args[1:]...) - app.Main(ctx, config) + app.Main(ctx, msg, config) panic("unreachable") }) @@ -78,9 +91,9 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command { passwd *user.User passwdOnce sync.Once passwdFunc = func() { - us := strconv.Itoa(app.HsuUid(new(app.Hsu).MustID(), flagIdentity)) + us := strconv.Itoa(app.HsuUid(new(app.Hsu).MustIDMsg(msg), flagIdentity)) if u, err := user.LookupId(us); err != nil { - hlog.Verbosef("cannot look up uid %s", us) + msg.Verbosef("cannot look up uid %s", us) passwd = &user.User{ Uid: us, Gid: us, @@ -162,7 +175,7 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command { } } - app.Main(ctx, config) + app.Main(ctx, msg, config) panic("unreachable") }). Flag(&flagDBusConfigSession, "dbus-config", command.StringFlag("builtin"), @@ -198,13 +211,13 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command { c.NewCommand("show", "Show live or local app configuration", func(args []string) error { switch len(args) { case 0: // system - printShowSystem(os.Stdout, flagShort, flagJSON) + printShowSystem(msg, os.Stdout, flagShort, flagJSON) case 1: // instance name := args[0] - config, entry := tryShort(name) + config, entry := tryShort(msg, name) if config == nil { - config = tryPath(name) + config = tryPath(msg, name) } printShowInstance(os.Stdout, time.Now().UTC(), entry, config, flagShort, flagJSON) @@ -219,8 +232,8 @@ func buildCommand(ctx context.Context, out io.Writer) command.Command { var flagShort bool c.NewCommand("ps", "List active instances", func(args []string) error { var sc hst.Paths - app.CopyPaths(&sc, new(app.Hsu).MustID()) - printPs(os.Stdout, time.Now().UTC(), state.NewMulti(sc.RunDirPath.String()), flagShort, flagJSON) + app.CopyPaths(msg, &sc, new(app.Hsu).MustID()) + printPs(os.Stdout, time.Now().UTC(), state.NewMulti(msg, sc.RunDirPath.String()), flagShort, flagJSON) return errSuccess }).Flag(&flagShort, "short", command.BoolFlag(false), "Print instance id") } diff --git a/cmd/hakurei/command_test.go b/cmd/hakurei/command_test.go index 8ce9a23d..dd16b250 100644 --- a/cmd/hakurei/command_test.go +++ b/cmd/hakurei/command_test.go @@ -7,6 +7,7 @@ import ( "testing" "hakurei.app/command" + "hakurei.app/container" ) func TestHelp(t *testing.T) { @@ -68,7 +69,7 @@ Flags: for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { out := new(bytes.Buffer) - c := buildCommand(t.Context(), out) + c := buildCommand(t.Context(), container.NewMsg(nil), new(earlyHardeningErrs), out) if err := c.Parse(tc.args); !errors.Is(err, command.ErrHelp) && !errors.Is(err, flag.ErrHelp) { t.Errorf("Parse: error = %v; want %v", err, command.ErrHelp) diff --git a/cmd/hakurei/main.go b/cmd/hakurei/main.go index 2b9193b6..5cc8224d 100644 --- a/cmd/hakurei/main.go +++ b/cmd/hakurei/main.go @@ -13,8 +13,6 @@ import ( "syscall" "hakurei.app/container" - "hakurei.app/internal" - "hakurei.app/internal/hlog" ) var ( @@ -24,20 +22,20 @@ var ( license string ) -func init() { hlog.Prepare("hakurei") } +// earlyHardeningErrs are errors collected while setting up early hardening feature. +type earlyHardeningErrs struct{ yamaLSM, dumpable error } func main() { // early init path, skips root check and duplicate PR_SET_DUMPABLE - container.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallOutput) + container.TryArgv0(nil) - if err := container.SetPtracer(0); err != nil { - hlog.Verbosef("cannot enable ptrace protection via Yama LSM: %v", err) - // not fatal: this program runs as the privileged user - } + log.SetPrefix("hakurei: ") + log.SetFlags(0) + msg := container.NewMsg(log.Default()) - if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil { - log.Printf("cannot set SUID_DUMP_DISABLE: %s", err) - // not fatal: this program runs as the privileged user + early := earlyHardeningErrs{ + yamaLSM: container.SetPtracer(0), + dumpable: container.SetDumpable(container.SUID_DUMP_DISABLE), } if os.Geteuid() == 0 { @@ -48,10 +46,10 @@ func main() { syscall.SIGINT, syscall.SIGTERM) defer stop() // unreachable - buildCommand(ctx, os.Stderr).MustParse(os.Args[1:], func(err error) { - hlog.Verbosef("command returned %v", err) + buildCommand(ctx, msg, &early, os.Stderr).MustParse(os.Args[1:], func(err error) { + msg.Verbosef("command returned %v", err) if errors.Is(err, errSuccess) { - hlog.BeforeExit() + msg.BeforeExit() os.Exit(0) } // this catches faulty command handlers that fail to return before this point diff --git a/cmd/hakurei/parse.go b/cmd/hakurei/parse.go index 897382ea..3825ca1f 100644 --- a/cmd/hakurei/parse.go +++ b/cmd/hakurei/parse.go @@ -10,20 +10,20 @@ import ( "strings" "syscall" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal/app" "hakurei.app/internal/app/state" - "hakurei.app/internal/hlog" ) -func tryPath(name string) (config *hst.Config) { +func tryPath(msg container.Msg, name string) (config *hst.Config) { var r io.Reader config = new(hst.Config) if name != "-" { - r = tryFd(name) + r = tryFd(msg, name) if r == nil { - hlog.Verbose("load configuration from file") + msg.Verbose("load configuration from file") if f, err := os.Open(name); err != nil { log.Fatalf("cannot access configuration file %q: %s", name, err) @@ -49,14 +49,14 @@ func tryPath(name string) (config *hst.Config) { return } -func tryFd(name string) io.ReadCloser { +func tryFd(msg container.Msg, name string) io.ReadCloser { if v, err := strconv.Atoi(name); err != nil { if !errors.Is(err, strconv.ErrSyntax) { - hlog.Verbosef("name cannot be interpreted as int64: %v", err) + msg.Verbosef("name cannot be interpreted as int64: %v", err) } return nil } else { - hlog.Verbosef("trying config stream from %d", v) + msg.Verbosef("trying config stream from %d", v) fd := uintptr(v) if _, _, errno := syscall.Syscall(syscall.SYS_FCNTL, fd, syscall.F_GETFD, 0); errno != 0 { if errors.Is(errno, syscall.EBADF) { @@ -68,7 +68,7 @@ func tryFd(name string) io.ReadCloser { } } -func tryShort(name string) (config *hst.Config, entry *state.State) { +func tryShort(msg container.Msg, name string) (config *hst.Config, entry *state.State) { likePrefix := false if len(name) <= 32 { likePrefix = true @@ -86,11 +86,11 @@ func tryShort(name string) (config *hst.Config, entry *state.State) { // try to match from state store if likePrefix && len(name) >= 8 { - hlog.Verbose("argument looks like prefix") + msg.Verbose("argument looks like prefix") var sc hst.Paths - app.CopyPaths(&sc, new(app.Hsu).MustID()) - s := state.NewMulti(sc.RunDirPath.String()) + app.CopyPaths(msg, &sc, new(app.Hsu).MustID()) + s := state.NewMulti(msg, sc.RunDirPath.String()) if entries, err := state.Join(s); err != nil { log.Printf("cannot join store: %v", err) // drop to fetch from file @@ -104,7 +104,7 @@ func tryShort(name string) (config *hst.Config, entry *state.State) { break } - hlog.Verbosef("instance %s skipped", v) + msg.Verbosef("instance %s skipped", v) } } } diff --git a/cmd/hakurei/print.go b/cmd/hakurei/print.go index 938517b8..2c575386 100644 --- a/cmd/hakurei/print.go +++ b/cmd/hakurei/print.go @@ -11,18 +11,19 @@ import ( "text/tabwriter" "time" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal/app" "hakurei.app/internal/app/state" "hakurei.app/system/dbus" ) -func printShowSystem(output io.Writer, short, flagJSON bool) { +func printShowSystem(msg container.Msg, output io.Writer, short, flagJSON bool) { t := newPrinter(output) defer t.MustFlush() info := &hst.Info{User: new(app.Hsu).MustID()} - app.CopyPaths(&info.Paths, info.User) + app.CopyPaths(msg, &info.Paths, info.User) if flagJSON { printJSON(output, short, info) diff --git a/cmd/hpkg/main.go b/cmd/hpkg/main.go index 2ae646c3..03247aac 100644 --- a/cmd/hpkg/main.go +++ b/cmd/hpkg/main.go @@ -13,22 +13,21 @@ import ( "hakurei.app/command" "hakurei.app/container" "hakurei.app/hst" - "hakurei.app/internal" - "hakurei.app/internal/hlog" ) var ( errSuccess = errors.New("success") ) -func init() { - hlog.Prepare("hpkg") +func main() { + log.SetPrefix("hpkg: ") + log.SetFlags(0) + msg := container.NewMsg(log.Default()) + if err := os.Setenv("SHELL", pathShell.String()); err != nil { log.Fatalf("cannot set $SHELL: %v", err) } -} -func main() { if os.Geteuid() == 0 { log.Fatal("this program must not run as root") } @@ -41,7 +40,7 @@ func main() { flagVerbose bool flagDropShell bool ) - c := command.New(os.Stderr, log.Printf, "hpkg", func([]string) error { internal.InstallOutput(flagVerbose); return nil }). + c := command.New(os.Stderr, log.Printf, "hpkg", func([]string) error { msg.SwapVerbose(flagVerbose); return nil }). Flag(&flagVerbose, "v", command.BoolFlag(false), "Print debug messages to the console"). Flag(&flagDropShell, "s", command.BoolFlag(false), "Drop to a shell in place of next hakurei action") @@ -90,12 +89,12 @@ func main() { } cleanup := func() { // should be faster than a native implementation - mustRun(chmod, "-R", "+w", workDir.String()) - mustRun(rm, "-rf", workDir.String()) + mustRun(msg, chmod, "-R", "+w", workDir.String()) + mustRun(msg, rm, "-rf", workDir.String()) } beforeRunFail.Store(&cleanup) - mustRun(tar, "-C", workDir.String(), "-xf", pkgPath) + mustRun(msg, tar, "-C", workDir.String(), "-xf", pkgPath) /* Parse bundle and app metadata, do pre-install checks. @@ -148,10 +147,10 @@ func main() { } // sec: should compare version string - hlog.Verbosef("installing application %q version %q over local %q", + msg.Verbosef("installing application %q version %q over local %q", bundle.ID, bundle.Version, a.Version) } else { - hlog.Verbosef("application %q clean installation", bundle.ID) + msg.Verbosef("application %q clean installation", bundle.ID) // sec: should install credentials } @@ -159,7 +158,7 @@ func main() { Setup steps for files owned by the target user. */ - withCacheDir(ctx, "install", []string{ + withCacheDir(ctx, msg, "install", []string{ // export inner bundle path in the environment "export BUNDLE=" + hst.Tmp + "/bundle", // replace inner /etc @@ -181,7 +180,7 @@ func main() { }, workDir, bundle, pathSet, flagDropShell, cleanup) if bundle.GPU { - withCacheDir(ctx, "mesa-wrappers", []string{ + withCacheDir(ctx, msg, "mesa-wrappers", []string{ // link nixGL mesa wrappers "mkdir -p nix/.nixGL", "ln -s " + bundle.Mesa + "/bin/nixGLIntel nix/.nixGL/nixGL", @@ -193,7 +192,7 @@ func main() { Activate home-manager generation. */ - withNixDaemon(ctx, "activate", []string{ + withNixDaemon(ctx, msg, "activate", []string{ // clean up broken links "mkdir -p .local/state/{nix,home-manager}", "chmod -R +w .local/state/{nix,home-manager}", @@ -261,7 +260,7 @@ func main() { */ if a.GPU && flagAutoDrivers { - withNixDaemon(ctx, "nix-gl", []string{ + withNixDaemon(ctx, msg, "nix-gl", []string{ "mkdir -p /nix/.nixGL/auto", "rm -rf /nix/.nixGL/auto", "export NIXPKGS_ALLOW_UNFREE=1", @@ -316,7 +315,7 @@ func main() { Spawn app. */ - mustRunApp(ctx, config, func() {}) + mustRunApp(ctx, msg, config, func() {}) return errSuccess }). Flag(&flagDropShellNixGL, "s", command.BoolFlag(false), "Drop to a shell on nixGL build"). @@ -324,9 +323,9 @@ func main() { } c.MustParse(os.Args[1:], func(err error) { - hlog.Verbosef("command returned %v", err) + msg.Verbosef("command returned %v", err) if errors.Is(err, errSuccess) { - hlog.BeforeExit() + msg.BeforeExit() os.Exit(0) } }) diff --git a/cmd/hpkg/paths.go b/cmd/hpkg/paths.go index 0964a550..ee4b7e71 100644 --- a/cmd/hpkg/paths.go +++ b/cmd/hpkg/paths.go @@ -9,7 +9,6 @@ import ( "hakurei.app/container" "hakurei.app/hst" - "hakurei.app/internal/hlog" ) const bash = "bash" @@ -51,8 +50,8 @@ func lookPath(file string) string { var beforeRunFail = new(atomic.Pointer[func()]) -func mustRun(name string, arg ...string) { - hlog.Verbosef("spawning process: %q %q", name, arg) +func mustRun(msg container.Msg, name string, arg ...string) { + msg.Verbosef("spawning process: %q %q", name, arg) cmd := exec.Command(name, arg...) cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr if err := cmd.Run(); err != nil { diff --git a/cmd/hpkg/proc.go b/cmd/hpkg/proc.go index d374e13d..1bbf5b49 100644 --- a/cmd/hpkg/proc.go +++ b/cmd/hpkg/proc.go @@ -9,14 +9,14 @@ import ( "os" "os/exec" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal" - "hakurei.app/internal/hlog" ) var hakureiPath = internal.MustHakureiPath() -func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) { +func mustRunApp(ctx context.Context, msg container.Msg, config *hst.Config, beforeFail func()) { var ( cmd *exec.Cmd st io.WriteCloser @@ -26,10 +26,10 @@ func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) { beforeFail() log.Fatalf("cannot pipe: %v", err) } else { - if hlog.Load() { - cmd = exec.CommandContext(ctx, hakureiPath, "-v", "app", "3") + if msg.IsVerbose() { + cmd = exec.CommandContext(ctx, hakureiPath.String(), "-v", "app", "3") } else { - cmd = exec.CommandContext(ctx, hakureiPath, "app", "3") + cmd = exec.CommandContext(ctx, hakureiPath.String(), "app", "3") } cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr cmd.ExtraFiles = []*os.File{r} @@ -51,7 +51,8 @@ func mustRunApp(ctx context.Context, config *hst.Config, beforeFail func()) { var exitError *exec.ExitError if errors.As(err, &exitError) { beforeFail() - internal.Exit(exitError.ExitCode()) + msg.BeforeExit() + os.Exit(exitError.ExitCode()) } else { beforeFail() log.Fatalf("cannot wait: %v", err) diff --git a/cmd/hpkg/with.go b/cmd/hpkg/with.go index 1895a06f..578d5251 100644 --- a/cmd/hpkg/with.go +++ b/cmd/hpkg/with.go @@ -2,20 +2,21 @@ package main import ( "context" + "os" "strings" "hakurei.app/container" "hakurei.app/container/seccomp" "hakurei.app/hst" - "hakurei.app/internal" ) func withNixDaemon( ctx context.Context, + msg container.Msg, action string, command []string, net bool, updateConfig func(config *hst.Config) *hst.Config, app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func(), ) { - mustRunAppDropShell(ctx, updateConfig(&hst.Config{ + mustRunAppDropShell(ctx, msg, updateConfig(&hst.Config{ ID: app.ID, Path: pathShell, @@ -61,9 +62,10 @@ func withNixDaemon( func withCacheDir( ctx context.Context, + msg container.Msg, action string, command []string, workDir *container.Absolute, app *appInfo, pathSet *appPathSet, dropShell bool, beforeFail func()) { - mustRunAppDropShell(ctx, &hst.Config{ + mustRunAppDropShell(ctx, msg, &hst.Config{ ID: app.ID, Path: pathShell, @@ -97,12 +99,13 @@ func withCacheDir( }, dropShell, beforeFail) } -func mustRunAppDropShell(ctx context.Context, config *hst.Config, dropShell bool, beforeFail func()) { +func mustRunAppDropShell(ctx context.Context, msg container.Msg, config *hst.Config, dropShell bool, beforeFail func()) { if dropShell { config.Args = []string{bash, "-l"} - mustRunApp(ctx, config, beforeFail) + mustRunApp(ctx, msg, config, beforeFail) beforeFail() - internal.Exit(0) + msg.BeforeExit() + os.Exit(0) } - mustRunApp(ctx, config, beforeFail) + mustRunApp(ctx, msg, config, beforeFail) } |
