diff options
Diffstat (limited to 'internal/app')
| -rw-r--r-- | internal/app/app.go | 7 | ||||
| -rw-r--r-- | internal/app/app_stub_test.go | 8 | ||||
| -rw-r--r-- | internal/app/app_test.go | 8 | ||||
| -rw-r--r-- | internal/app/container.go | 21 | ||||
| -rw-r--r-- | internal/app/dispatcher.go | 21 | ||||
| -rw-r--r-- | internal/app/finalise.go | 24 | ||||
| -rw-r--r-- | internal/app/hsu.go | 12 | ||||
| -rw-r--r-- | internal/app/paths.go | 8 | ||||
| -rw-r--r-- | internal/app/process.go | 46 | ||||
| -rw-r--r-- | internal/app/shim.go | 14 | ||||
| -rw-r--r-- | internal/app/state/multi.go | 52 | ||||
| -rw-r--r-- | internal/app/state/multi_test.go | 6 | ||||
| -rw-r--r-- | internal/app/state/state.go | 6 | ||||
| -rw-r--r-- | internal/app/state/state_test.go | 32 |
14 files changed, 135 insertions, 130 deletions
diff --git a/internal/app/app.go b/internal/app/app.go index b5331568..c5a3e0f4 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -6,23 +6,24 @@ import ( "log" "os" + "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal/app/state" ) // Main runs an app according to [hst.Config] and terminates. Main does not return. -func Main(ctx context.Context, config *hst.Config) { +func Main(ctx context.Context, msg container.Msg, config *hst.Config) { var id state.ID if err := state.NewAppID(&id); err != nil { log.Fatal(err) } seal := outcome{id: &stringPair[state.ID]{id, id.String()}, syscallDispatcher: direct{}} - if err := seal.finalise(ctx, config); err != nil { + if err := seal.finalise(ctx, msg, config); err != nil { printMessageError("cannot seal app:", err) os.Exit(1) } - seal.main() + seal.main(msg) panic("unreachable") } diff --git a/internal/app/app_stub_test.go b/internal/app/app_stub_test.go index ff218492..8d33cd15 100644 --- a/internal/app/app_stub_test.go +++ b/internal/app/app_stub_test.go @@ -6,6 +6,8 @@ import ( "log" "os/exec" "os/user" + + "hakurei.app/container" ) type stubNixOS struct { @@ -187,10 +189,10 @@ func (k *stubNixOS) cmdOutput(cmd *exec.Cmd) ([]byte, error) { } } -func (k *stubNixOS) overflowUid() int { return 65534 } -func (k *stubNixOS) overflowGid() int { return 65534 } +func (k *stubNixOS) overflowUid(container.Msg) int { return 65534 } +func (k *stubNixOS) overflowGid(container.Msg) int { return 65534 } -func (k *stubNixOS) mustHsuPath() string { return "/proc/nonexistent/hsu" } +func (k *stubNixOS) mustHsuPath() *container.Absolute { return m("/proc/nonexistent/hsu") } func (k *stubNixOS) fatalf(format string, v ...any) { panic(fmt.Sprintf(format, v...)) } diff --git a/internal/app/app_test.go b/internal/app/app_test.go index 005b721d..fa85c461 100644 --- a/internal/app/app_test.go +++ b/internal/app/app_test.go @@ -37,7 +37,7 @@ func TestApp(t *testing.T) { 0xbd, 0x01, 0x78, 0x0e, 0xb9, 0xa6, 0x07, 0xac, }, - system.New(context.TODO(), 1000000). + system.New(context.TODO(), container.NewMsg(nil), 1000000). Ensure("/tmp/hakurei.0", 0711). Ensure("/tmp/hakurei.0/runtime", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime", acl.Execute). Ensure("/tmp/hakurei.0/runtime/0", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime/0", acl.Read, acl.Write, acl.Execute). @@ -129,7 +129,7 @@ func TestApp(t *testing.T) { 0x82, 0xd4, 0x13, 0x36, 0x9b, 0x64, 0xce, 0x7c, }, - system.New(context.TODO(), 1000009). + system.New(context.TODO(), container.NewMsg(nil), 1000009). Ensure("/tmp/hakurei.0", 0711). Ensure("/tmp/hakurei.0/runtime", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime", acl.Execute). Ensure("/tmp/hakurei.0/runtime/9", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime/9", acl.Read, acl.Write, acl.Execute). @@ -280,7 +280,7 @@ func TestApp(t *testing.T) { 0x4c, 0xf0, 0x73, 0xbd, 0xb4, 0x6e, 0xb5, 0xc1, }, - system.New(context.TODO(), 1000001). + system.New(context.TODO(), container.NewMsg(nil), 1000001). Ensure("/tmp/hakurei.0", 0711). Ensure("/tmp/hakurei.0/runtime", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime", acl.Execute). Ensure("/tmp/hakurei.0/runtime/1", 0700).UpdatePermType(system.User, "/tmp/hakurei.0/runtime/1", acl.Read, acl.Write, acl.Execute). @@ -377,7 +377,7 @@ func TestApp(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Run("finalise", func(t *testing.T) { seal := outcome{syscallDispatcher: tc.k, id: &stringPair[state.ID]{tc.id, tc.id.String()}} - err := seal.finalise(t.Context(), tc.config) + err := seal.finalise(t.Context(), container.NewMsg(nil), tc.config) if err != nil { if s, ok := container.GetErrorMessage(err); !ok { t.Fatalf("Seal: error = %v", err) diff --git a/internal/app/container.go b/internal/app/container.go index d406d211..ae9dbf33 100644 --- a/internal/app/container.go +++ b/internal/app/container.go @@ -20,6 +20,7 @@ const preallocateOpsCount = 1 << 5 // newContainer initialises [container.Params] via [hst.ContainerConfig]. // Note that remaining container setup must be queued by the caller. func newContainer( + msg container.Msg, k syscallDispatcher, s *hst.ContainerConfig, prefix string, @@ -73,8 +74,8 @@ func newContainer( params.Gid = k.getgid() *gid = params.Gid } else { - *uid = k.overflowUid() - *gid = k.overflowGid() + *uid = k.overflowUid(msg) + *gid = k.overflowGid(msg) } filesystem := s.Filesystem @@ -126,11 +127,11 @@ func newContainer( // get parent dir of socket dir := path.Dir(pair[1]) if dir == "." || dir == container.FHSRoot { - k.verbosef("dbus socket %q is in an unusual location", pair[1]) + msg.Verbosef("dbus socket %q is in an unusual location", pair[1]) } hidePaths = append(hidePaths, dir) } else { - k.verbosef("dbus socket %q is not absolute", pair[1]) + msg.Verbosef("dbus socket %q is not absolute", pair[1]) } } } @@ -138,7 +139,7 @@ func newContainer( } hidePathMatch := make([]bool, len(hidePaths)) for i := range hidePaths { - if err := evalSymlinks(k, &hidePaths[i]); err != nil { + if err := evalSymlinks(msg, k, &hidePaths[i]); err != nil { return nil, nil, err } } @@ -178,7 +179,7 @@ func newContainer( if autoroot != nil { for _, ent := range autoRootEntries { name := ent.Name() - if container.IsAutoRootBindable(name) { + if container.IsAutoRootBindable(msg, name) { hidePathSource = append(hidePathSource, autoroot.Source.Append(name)) } } @@ -193,7 +194,7 @@ func newContainer( } hidePathSourceEval[i] = [2]string{a.String(), a.String()} - if err := evalSymlinks(k, &hidePathSourceEval[i][0]); err != nil { + if err := evalSymlinks(msg, k, &hidePathSourceEval[i][0]); err != nil { return nil, nil, err } } @@ -209,7 +210,7 @@ func newContainer( return nil, nil, err } else if ok { hidePathMatch[i] = true - k.verbosef("hiding path %q from %q", hidePaths[i], p[1]) + msg.Verbosef("hiding path %q from %q", hidePaths[i], p[1]) } } } @@ -241,12 +242,12 @@ func newContainer( } // evalSymlinks calls syscallDispatcher.evalSymlinks but discards errors unwrapping to [fs.ErrNotExist]. -func evalSymlinks(k syscallDispatcher, v *string) error { +func evalSymlinks(msg container.Msg, k syscallDispatcher, v *string) error { if p, err := k.evalSymlinks(*v); err != nil { if !errors.Is(err, fs.ErrNotExist) { return err } - k.verbosef("path %q does not yet exist", *v) + msg.Verbosef("path %q does not yet exist", *v) } else { *v = p } diff --git a/internal/app/dispatcher.go b/internal/app/dispatcher.go index deecfa92..8ab60734 100644 --- a/internal/app/dispatcher.go +++ b/internal/app/dispatcher.go @@ -9,7 +9,6 @@ import ( "hakurei.app/container" "hakurei.app/internal" - "hakurei.app/internal/hlog" ) // syscallDispatcher provides methods that make state-dependent system calls as part of their behaviour. @@ -45,19 +44,15 @@ type syscallDispatcher interface { cmdOutput(cmd *exec.Cmd) ([]byte, error) // overflowUid provides [container.OverflowUid]. - overflowUid() int + overflowUid(msg container.Msg) int // overflowGid provides [container.OverflowGid]. - overflowGid() int + overflowGid(msg container.Msg) int // mustHsuPath provides [internal.MustHsuPath]. - mustHsuPath() string + mustHsuPath() *container.Absolute // fatalf provides [log.Fatalf]. fatalf(format string, v ...any) - - isVerbose() bool - verbose(v ...any) - verbosef(format string, v ...any) } // direct implements syscallDispatcher on the current kernel. @@ -87,13 +82,9 @@ func (direct) lookupGroupId(name string) (gid string, err error) { func (direct) cmdOutput(cmd *exec.Cmd) ([]byte, error) { return cmd.Output() } -func (direct) overflowUid() int { return container.OverflowUid() } -func (direct) overflowGid() int { return container.OverflowGid() } +func (direct) overflowUid(msg container.Msg) int { return container.OverflowUid(msg) } +func (direct) overflowGid(msg container.Msg) int { return container.OverflowGid(msg) } -func (direct) mustHsuPath() string { return internal.MustHsuPath() } +func (direct) mustHsuPath() *container.Absolute { return internal.MustHsuPath() } func (direct) fatalf(format string, v ...any) { log.Fatalf(format, v...) } - -func (k direct) isVerbose() bool { return hlog.Load() } -func (direct) verbose(v ...any) { hlog.Verbose(v...) } -func (direct) verbosef(format string, v ...any) { hlog.Verbosef(format, v...) } diff --git a/internal/app/finalise.go b/internal/app/finalise.go index 2a0e5e6e..d2c685f4 100644 --- a/internal/app/finalise.go +++ b/internal/app/finalise.go @@ -20,7 +20,6 @@ import ( "hakurei.app/container" "hakurei.app/hst" "hakurei.app/internal/app/state" - "hakurei.app/internal/hlog" "hakurei.app/system" "hakurei.app/system/acl" "hakurei.app/system/dbus" @@ -120,7 +119,7 @@ type hsuUser struct { username string } -func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { +func (k *outcome) finalise(ctx context.Context, msg container.Msg, config *hst.Config) error { const ( home = "HOME" shell = "SHELL" @@ -183,7 +182,7 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { } else if !isValidUsername(k.user.username) { return newWithMessage(fmt.Sprintf("invalid user name %q", k.user.username)) } - k.user.uid = newInt(HsuUid(hsu.MustID(), k.user.identity.unwrap())) + k.user.uid = newInt(HsuUid(hsu.MustIDMsg(msg), k.user.identity.unwrap())) k.user.supp = make([]string, len(config.Groups)) for i, name := range config.Groups { @@ -201,7 +200,7 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { // permissive defaults if config.Container == nil { - hlog.Verbose("container configuration not supplied, PROCEED WITH CAUTION") + msg.Verbose("container configuration not supplied, PROCEED WITH CAUTION") if config.Shell == nil { config.Shell = container.AbsFHSRoot.Append("bin", "sh") @@ -276,13 +275,14 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { // TODO(ophestra): revert this after params to shim share := &shareHost{seal: k} - copyPaths(k.syscallDispatcher, &share.sc, hsu.MustID()) + copyPaths(k.syscallDispatcher, msg, &share.sc, hsu.MustIDMsg(msg)) + msg.Verbosef("process share directory at %q, runtime directory at %q", share.sc.SharePath, share.sc.RunDirPath) var mapuid, mapgid *stringPair[int] { var uid, gid int var err error - k.container, k.env, err = newContainer(k, config.Container, k.id.String(), &share.sc, &uid, &gid) + k.container, k.env, err = newContainer(msg, k, config.Container, k.id.String(), &share.sc, &uid, &gid) k.waitDelay = config.Container.WaitDelay if err != nil { return &hst.AppError{Step: "initialise container configuration", Err: err} @@ -307,7 +307,7 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { k.env[xdgSessionType] = "tty" k.runDirPath = share.sc.RunDirPath - k.sys = system.New(k.ctx, k.user.uid.unwrap()) + k.sys = system.New(k.ctx, msg, k.user.uid.unwrap()) k.sys.Ensure(share.sc.SharePath.String(), 0711) { @@ -357,7 +357,7 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { // outer wayland socket (usually `/run/user/%d/wayland-%d`) var socketPath *container.Absolute if name, ok := k.lookupEnv(wayland.WaylandDisplay); !ok { - hlog.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName) + msg.Verbose(wayland.WaylandDisplay + " is not set, assuming " + wayland.FallbackName) socketPath = share.sc.RuntimePath.Append(wayland.FallbackName) } else if a, err := container.NewAbs(name); err != nil { socketPath = share.sc.RuntimePath.Append(name) @@ -379,7 +379,7 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { k.sys.Wayland(&k.sync, outerPath.String(), socketPath.String(), appID, k.id.String()) k.container.Bind(outerPath, innerPath, 0) } else { // bind mount wayland socket (insecure) - hlog.Verbose("direct wayland access, PROCEED WITH CAUTION") + msg.Verbose("direct wayland access, PROCEED WITH CAUTION") share.ensureRuntimeDir() k.container.Bind(socketPath, innerPath, 0) k.sys.UpdatePermType(system.EWayland, socketPath.String(), acl.Read, acl.Write, acl.Execute) @@ -520,7 +520,7 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { k.container.PlaceP(innerDst, &payload) k.sys.CopyFile(payload, paCookiePath.String(), 256, 256) } else { - hlog.Verbose("cannot locate PulseAudio cookie (tried " + + msg.Verbose("cannot locate PulseAudio cookie (tried " + "$PULSE_COOKIE, " + "$XDG_CONFIG_HOME/pulse/cookie, " + "$HOME/.pulse-cookie)") @@ -596,8 +596,8 @@ func (k *outcome) finalise(ctx context.Context, config *hst.Config) error { } slices.Sort(k.container.Env) - if hlog.Load() { - hlog.Verbosef("created application seal for uid %s (%s) groups: %v, argv: %s, ops: %d", + if msg.IsVerbose() { + msg.Verbosef("created application seal for uid %s (%s) groups: %v, argv: %s, ops: %d", k.user.uid, k.user.username, config.Groups, k.container.Args, len(*k.container.Ops)) } diff --git a/internal/app/hsu.go b/internal/app/hsu.go index 0078ef0d..93b6bb8a 100644 --- a/internal/app/hsu.go +++ b/internal/app/hsu.go @@ -11,7 +11,6 @@ import ( "hakurei.app/container" "hakurei.app/hst" - "hakurei.app/internal/hlog" ) // Hsu caches responses from cmd/hsu. @@ -40,7 +39,7 @@ func (h *Hsu) ID() (int, error) { h.ensureDispatcher() h.idOnce.Do(func() { h.id = -1 - hsuPath := h.k.mustHsuPath() + hsuPath := h.k.mustHsuPath().String() cmd := exec.Command(hsuPath) cmd.Path = hsuPath @@ -71,7 +70,10 @@ func (h *Hsu) ID() (int, error) { } // MustID calls [Hsu.ID] and terminates on error. -func (h *Hsu) MustID() int { +func (h *Hsu) MustID() int { return h.MustIDMsg(nil) } + +// MustIDMsg implements MustID with a custom [container.Msg]. +func (h *Hsu) MustIDMsg(msg container.Msg) int { id, err := h.ID() if err == nil { return id @@ -79,7 +81,9 @@ func (h *Hsu) MustID() int { const fallback = "cannot retrieve user id from setuid wrapper:" if errors.Is(err, ErrHsuAccess) { - hlog.Verbose("*"+fallback, err) + if msg != nil { + msg.Verbose("*"+fallback, err) + } os.Exit(1) return -0xdeadbeef } else if m, ok := container.GetErrorMessage(err); ok { diff --git a/internal/app/paths.go b/internal/app/paths.go index 3316a22e..442317cc 100644 --- a/internal/app/paths.go +++ b/internal/app/paths.go @@ -8,10 +8,10 @@ import ( ) // CopyPaths populates a [hst.Paths] struct. -func CopyPaths(v *hst.Paths, userid int) { copyPaths(direct{}, v, userid) } +func CopyPaths(msg container.Msg, v *hst.Paths, userid int) { copyPaths(direct{}, msg, v, userid) } // copyPaths populates a [hst.Paths] struct. -func copyPaths(k syscallDispatcher, v *hst.Paths, userid int) { +func copyPaths(k syscallDispatcher, msg container.Msg, v *hst.Paths, userid int) { const xdgRuntimeDir = "XDG_RUNTIME_DIR" if tempDir, err := container.NewAbs(k.tempdir()); err != nil { @@ -21,7 +21,7 @@ func copyPaths(k syscallDispatcher, v *hst.Paths, userid int) { } v.SharePath = v.TempDir.Append("hakurei." + strconv.Itoa(userid)) - k.verbosef("process share directory at %q", v.SharePath) + msg.Verbosef("process share directory at %q", v.SharePath) r, _ := k.lookupEnv(xdgRuntimeDir) if a, err := container.NewAbs(r); err != nil { @@ -32,5 +32,5 @@ func copyPaths(k syscallDispatcher, v *hst.Paths, userid int) { v.RuntimePath = a v.RunDirPath = v.RuntimePath.Append("hakurei") } - k.verbosef("runtime directory at %q", v.RunDirPath) + msg.Verbosef("runtime directory at %q", v.RunDirPath) } diff --git a/internal/app/process.go b/internal/app/process.go index 8ac05855..1b9c037b 100644 --- a/internal/app/process.go +++ b/internal/app/process.go @@ -15,7 +15,6 @@ import ( "hakurei.app/container" "hakurei.app/internal" "hakurei.app/internal/app/state" - "hakurei.app/internal/hlog" "hakurei.app/system" ) @@ -39,6 +38,7 @@ type mainState struct { cmdWait chan error k *outcome + container.Msg uintptr } @@ -55,7 +55,7 @@ func (ms mainState) beforeExit(isFault bool) { panic("attempting to call beforeExit twice") } ms.done = true - defer hlog.BeforeExit() + defer ms.BeforeExit() if isFault && ms.cancel != nil { ms.cancel() @@ -97,37 +97,37 @@ func (ms mainState) beforeExit(isFault bool) { } } - if hlog.Load() { + if ms.IsVerbose() { if !ok { if err != nil { - hlog.Verbosef("wait: %v", err) + ms.Verbosef("wait: %v", err) } } else { switch { case wstatus.Exited(): - hlog.Verbosef("process %d exited with code %d", ms.cmd.Process.Pid, wstatus.ExitStatus()) + ms.Verbosef("process %d exited with code %d", ms.cmd.Process.Pid, wstatus.ExitStatus()) case wstatus.CoreDump(): - hlog.Verbosef("process %d dumped core", ms.cmd.Process.Pid) + ms.Verbosef("process %d dumped core", ms.cmd.Process.Pid) case wstatus.Signaled(): - hlog.Verbosef("process %d got %s", ms.cmd.Process.Pid, wstatus.Signal()) + ms.Verbosef("process %d got %s", ms.cmd.Process.Pid, wstatus.Signal()) default: - hlog.Verbosef("process %d exited with status %#x", ms.cmd.Process.Pid, wstatus) + ms.Verbosef("process %d exited with status %#x", ms.cmd.Process.Pid, wstatus) } } } case <-waitDone: - hlog.Resume() + ms.Resume() // this is only reachable when shim did not exit within shimWaitTimeout, after its WaitDelay has elapsed. // This is different from the container failing to terminate within its timeout period, as that is enforced // by the shim. This path is instead reached when there is a lockup in shim preventing it from completing. log.Printf("process %d did not terminate", ms.cmd.Process.Pid) } - hlog.Resume() + ms.Resume() if ms.k.sync != nil { if err := ms.k.sync.Close(); err != nil { perror(err, "close wayland security context") @@ -170,7 +170,7 @@ func (ms mainState) beforeExit(isFault bool) { if l := len(states); l == 0 { ec |= system.User } else { - hlog.Verbosef("found %d instances, cleaning up without user-scoped operations", l) + ms.Verbosef("found %d instances, cleaning up without user-scoped operations", l) } // accumulate enablements of remaining launchers @@ -183,9 +183,9 @@ func (ms mainState) beforeExit(isFault bool) { } ec |= rt ^ (system.EWayland | system.EX11 | system.EDBus | system.EPulse) - if hlog.Load() { + if ms.IsVerbose() { if ec > 0 { - hlog.Verbose("reverting operations scope", system.TypeString(ec)) + ms.Verbose("reverting operations scope", system.TypeString(ec)) } } @@ -219,7 +219,7 @@ func (ms mainState) fatal(fallback string, ferr error) { } // main carries out outcome and terminates. main does not return. -func (k *outcome) main() { +func (k *outcome) main(msg container.Msg) { if !k.active.CompareAndSwap(false, true) { panic("outcome: attempted to run twice") } @@ -228,19 +228,19 @@ func (k *outcome) main() { hsuPath := internal.MustHsuPath() // ms.beforeExit required beyond this point - ms := &mainState{k: k} + ms := &mainState{Msg: msg, k: k} if err := k.sys.Commit(); err != nil { ms.fatal("cannot commit system setup:", err) } ms.uintptr |= mainNeedsRevert - ms.store = state.NewMulti(k.runDirPath.String()) + ms.store = state.NewMulti(msg, k.runDirPath.String()) ctx, cancel := context.WithCancel(k.ctx) defer cancel() ms.cancel = cancel - ms.cmd = exec.CommandContext(ctx, hsuPath) + ms.cmd = exec.CommandContext(ctx, hsuPath.String()) ms.cmd.Stdin, ms.cmd.Stdout, ms.cmd.Stderr = os.Stdin, os.Stdout, os.Stderr ms.cmd.Dir = container.FHSRoot // container init enters final working directory // shim runs in the same session as monitor; see shim.go for behaviour @@ -260,13 +260,13 @@ func (k *outcome) main() { } if len(k.user.supp) > 0 { - hlog.Verbosef("attaching supplementary group ids %s", k.user.supp) + msg.Verbosef("attaching supplementary group ids %s", k.user.supp) // interpreted by hsu ms.cmd.Env = append(ms.cmd.Env, "HAKUREI_GROUPS="+strings.Join(k.user.supp, " ")) } - hlog.Verbosef("setuid helper at %s", hsuPath) - hlog.Suspend() + msg.Verbosef("setuid helper at %s", hsuPath) + msg.Suspend() if err := ms.cmd.Start(); err != nil { ms.fatal("cannot start setuid wrapper:", err) } @@ -286,18 +286,18 @@ func (k *outcome) main() { os.Getpid(), k.waitDelay, k.container, - hlog.Load(), + msg.IsVerbose(), }) }() return }(): if err != nil { - hlog.Resume() + msg.Resume() ms.fatal("cannot transmit shim config:", err) } case <-ctx.Done(): - hlog.Resume() + msg.Resume() ms.fatal("shim context canceled:", newWithMessageError("shim setup canceled", ctx.Err())) } diff --git a/internal/app/shim.go b/internal/app/shim.go index e38104c9..8c860e1b 100644 --- a/internal/app/shim.go +++ b/internal/app/shim.go @@ -15,8 +15,6 @@ import ( "hakurei.app/container" "hakurei.app/container/seccomp" - "hakurei.app/internal" - "hakurei.app/internal/hlog" ) //#include "shim-signal.h" @@ -53,7 +51,9 @@ const ( // ShimMain is the main function of the shim process and runs as the unconstrained target user. func ShimMain() { - hlog.Prepare("shim") + log.SetPrefix("shim: ") + log.SetFlags(0) + msg := container.NewMsg(log.Default()) if err := container.SetDumpable(container.SUID_DUMP_DISABLE); err != nil { log.Fatalf("cannot set SUID_DUMP_DISABLE: %s", err) @@ -73,7 +73,7 @@ func ShimMain() { log.Fatalf("cannot receive shim setup params: %v", err) } else { - internal.InstallOutput(params.Verbose) + msg.SwapVerbose(params.Verbose) closeSetup = f } @@ -111,12 +111,12 @@ func ShimMain() { } // setup has not completed, terminate immediately - hlog.Resume() + msg.Resume() os.Exit(ShimExitRequest) return case 1: // got SIGCONT after adoption: monitor died before delivering signal - hlog.BeforeExit() + msg.BeforeExit() os.Exit(ShimExitOrphan) return @@ -144,7 +144,7 @@ func ShimMain() { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) cancelContainer.Store(&stop) - z := container.New(ctx) + z := container.New(ctx, msg) z.Params = *params.Container z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr diff --git a/internal/app/state/multi.go b/internal/app/state/multi.go index 9a400ac6..822cddcd 100644 --- a/internal/app/state/multi.go +++ b/internal/app/state/multi.go @@ -13,8 +13,8 @@ import ( "sync" "syscall" + "hakurei.app/container" "hakurei.app/hst" - "hakurei.app/internal/hlog" ) // fine-grained locking and access @@ -24,16 +24,17 @@ type multiStore struct { // initialised backends backends *sync.Map - lock sync.RWMutex + msg container.Msg + mu sync.RWMutex } func (s *multiStore) Do(identity int, f func(c Cursor)) (bool, error) { - s.lock.RLock() - defer s.lock.RUnlock() + s.mu.RLock() + defer s.mu.RUnlock() // load or initialise new backend b := new(multiBackend) - b.lock.Lock() + b.mu.Lock() if v, ok := s.backends.LoadOrStore(identity, b); ok { b = v.(*multiBackend) } else { @@ -52,7 +53,7 @@ func (s *multiStore) Do(identity int, f func(c Cursor)) (bool, error) { } else { b.lockfile = l } - b.lock.Unlock() + b.mu.Unlock() } // lock backend @@ -85,17 +86,17 @@ func (s *multiStore) List() ([]int, error) { for _, e := range entries { // skip non-directories if !e.IsDir() { - hlog.Verbosef("skipped non-directory entry %q", e.Name()) + s.msg.Verbosef("skipped non-directory entry %q", e.Name()) continue } // skip non-numerical names if v, err := strconv.Atoi(e.Name()); err != nil { - hlog.Verbosef("skipped non-aid entry %q", e.Name()) + s.msg.Verbosef("skipped non-aid entry %q", e.Name()) continue } else { if v < 0 || v > 9999 { - hlog.Verbosef("skipped out of bounds entry %q", e.Name()) + s.msg.Verbosef("skipped out of bounds entry %q", e.Name()) continue } @@ -107,8 +108,8 @@ func (s *multiStore) List() ([]int, error) { } func (s *multiStore) Close() error { - s.lock.Lock() - defer s.lock.Unlock() + s.mu.Lock() + defer s.mu.Unlock() var errs []error s.backends.Range(func(_, value any) bool { @@ -126,7 +127,7 @@ type multiBackend struct { // created/opened by prepare lockfile *os.File - lock sync.RWMutex + mu sync.RWMutex } func (b *multiBackend) filename(id *ID) string { @@ -169,8 +170,8 @@ func (b *multiBackend) unlockFile() error { // reads all launchers in simpleBackend // file contents are ignored if decode is false func (b *multiBackend) load(decode bool) (Entries, error) { - b.lock.RLock() - defer b.lock.RUnlock() + b.mu.RLock() + defer b.mu.RUnlock() // read directory contents, should only contain files named after ids var entries []os.DirEntry @@ -280,8 +281,8 @@ func (b *multiBackend) decodeState(r io.ReadSeeker, state *State) error { // Save writes process state to filesystem func (b *multiBackend) Save(state *State, configWriter io.WriterTo) error { - b.lock.Lock() - defer b.lock.Unlock() + b.mu.Lock() + defer b.mu.Unlock() if configWriter == nil && state.Config == nil { return ErrNoConfig @@ -336,8 +337,8 @@ func (b *multiBackend) encodeState(w io.WriteSeeker, state *State, configWriter } func (b *multiBackend) Destroy(id ID) error { - b.lock.Lock() - defer b.lock.Unlock() + b.mu.Lock() + defer b.mu.Unlock() return os.Remove(b.filename(&id)) } @@ -353,8 +354,8 @@ func (b *multiBackend) Len() (int, error) { } func (b *multiBackend) close() error { - b.lock.Lock() - defer b.lock.Unlock() + b.mu.Lock() + defer b.mu.Unlock() err := b.lockfile.Close() if err == nil || errors.Is(err, os.ErrInvalid) || errors.Is(err, os.ErrClosed) { @@ -364,9 +365,10 @@ func (b *multiBackend) close() error { } // NewMulti returns an instance of the multi-file store. -func NewMulti(runDir string) Store { - b := new(multiStore) - b.base = path.Join(runDir, "state") - b.backends = new(sync.Map) - return b +func NewMulti(msg container.Msg, runDir string) Store { + return &multiStore{ + msg: msg, + base: path.Join(runDir, "state"), + backends: new(sync.Map), + } } diff --git a/internal/app/state/multi_test.go b/internal/app/state/multi_test.go index d493a388..b8e1db9b 100644 --- a/internal/app/state/multi_test.go +++ b/internal/app/state/multi_test.go @@ -1,9 +1,13 @@ package state_test import ( + "log" "testing" + "hakurei.app/container" "hakurei.app/internal/app/state" ) -func TestMulti(t *testing.T) { testStore(t, state.NewMulti(t.TempDir())) } +func TestMulti(t *testing.T) { + testStore(t, state.NewMulti(container.NewMsg(log.New(log.Writer(), "multi: ", 0)), t.TempDir())) +} diff --git a/internal/app/state/state.go b/internal/app/state/state.go index 6a3a82db..59fbc8c7 100644 --- a/internal/app/state/state.go +++ b/internal/app/state/state.go @@ -19,9 +19,9 @@ type Store interface { // Cursor provided to f becomes invalid as soon as f returns. Do(identity int, f func(c Cursor)) (ok bool, err error) - // List queries the store and returns a list of aids known to the store. - // Note that some or all returned aids might not have any active apps. - List() (aids []int, err error) + // List queries the store and returns a list of identities known to the store. + // Note that some or all returned identities might not have any active apps. + List() (identities []int, err error) // Close releases any resources held by Store. Close() error diff --git a/internal/app/state/state_test.go b/internal/app/state/state_test.go index 0025918d..cc578652 100644 --- a/internal/app/state/state_test.go +++ b/internal/app/state/state_test.go @@ -16,10 +16,10 @@ import ( func testStore(t *testing.T, s state.Store) { t.Run("list empty store", func(t *testing.T) { - if aids, err := s.List(); err != nil { + if identities, err := s.List(); err != nil { t.Fatalf("List: error = %v", err) - } else if len(aids) != 0 { - t.Fatalf("List: aids = %#v", aids) + } else if len(identities) != 0 { + t.Fatalf("List: identities = %#v", identities) } }) @@ -39,22 +39,22 @@ func testStore(t *testing.T, s state.Store) { makeState(t, &tc[i].state, &tc[i].ct) } - do := func(aid int, f func(c state.Cursor)) { - if ok, err := s.Do(aid, f); err != nil { + do := func(identity int, f func(c state.Cursor)) { + if ok, err := s.Do(identity, f); err != nil { t.Fatalf("Do: ok = %v, error = %v", ok, err) } } - insert := func(i, aid int) { - do(aid, func(c state.Cursor) { + insert := func(i, identity int) { + do(identity, func(c state.Cursor) { if err := c.Save(&tc[i].state, &tc[i].ct); err != nil { t.Fatalf("Save(&tc[%v]): error = %v", i, err) } }) } - check := func(i, aid int) { - do(aid, func(c state.Cursor) { + check := func(i, identity int) { + do(identity, func(c state.Cursor) { if entries, err := c.Load(); err != nil { t.Fatalf("Load: error = %v", err) } else if got, ok := entries[tc[i].state.ID]; !ok { @@ -81,7 +81,7 @@ func testStore(t *testing.T, s state.Store) { insert(insertEntryNoCheck, 0) }) - t.Run("insert entry different aid", func(t *testing.T) { + t.Run("insert entry different identity", func(t *testing.T) { insert(insertEntryOtherApp, 1) check(insertEntryOtherApp, 1) }) @@ -90,14 +90,14 @@ func testStore(t *testing.T, s state.Store) { check(insertEntryNoCheck, 0) }) - t.Run("list aids", func(t *testing.T) { - if aids, err := s.List(); err != nil { + t.Run("list identities", func(t *testing.T) { + if identities, err := s.List(); err != nil { t.Fatalf("List: error = %v", err) } else { - slices.Sort(aids) + slices.Sort(identities) want := []int{0, 1} - if !slices.Equal(aids, want) { - t.Fatalf("List() = %#v, want %#v", aids, want) + if !slices.Equal(identities, want) { + t.Fatalf("List() = %#v, want %#v", identities, want) } } }) @@ -110,7 +110,7 @@ func testStore(t *testing.T, s state.Store) { } }) - t.Run("clear aid 1", func(t *testing.T) { + t.Run("clear identity 1", func(t *testing.T) { do(1, func(c state.Cursor) { if err := c.Destroy(tc[insertEntryOtherApp].state.ID); err != nil { t.Fatalf("Destroy: error = %v", err) |
