aboutsummaryrefslogtreecommitdiffhomepage
path: root/container
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-09-29 02:33:10 +0900
committerOphestra <cat@gensokyo.uk>2025-09-29 06:11:47 +0900
commit46cd3a28c83e93b5d66c52d06a8366c11910d5c0 (patch)
tree6e25c5078b5cd9de78b7a6c9b253f1132358812b /container
parentad1bc6794f0053c3e63eab408a0d530d53e8b51c (diff)
container: remove global msg
This frees all container instances of side effects. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container')
-rw-r--r--container/autoroot.go4
-rw-r--r--container/autoroot_test.go23
-rw-r--r--container/container.go29
-rw-r--r--container/container_test.go14
-rw-r--r--container/dispatcher.go50
-rw-r--r--container/dispatcher_test.go62
-rw-r--r--container/executable.go9
-rw-r--r--container/executable_test.go2
-rw-r--r--container/init.go155
-rw-r--r--container/init_test.go161
-rw-r--r--container/initbind.go8
-rw-r--r--container/initdev.go4
-rw-r--r--container/initplace.go1
-rw-r--r--container/initremount.go4
-rw-r--r--container/mount.go17
-rw-r--r--container/mount_test.go82
-rw-r--r--container/msg.go89
-rw-r--r--container/msg_test.go239
-rw-r--r--container/output_test.go52
-rw-r--r--container/sysctl.go41
20 files changed, 584 insertions, 462 deletions
diff --git a/container/autoroot.go b/container/autoroot.go
index c37e49bc..ee8bcbd3 100644
--- a/container/autoroot.go
+++ b/container/autoroot.go
@@ -34,7 +34,7 @@ func (r *AutoRootOp) early(state *setupState, k syscallDispatcher) error {
r.resolved = make([]*BindMountOp, 0, len(d))
for _, ent := range d {
name := ent.Name()
- if IsAutoRootBindable(name) {
+ if IsAutoRootBindable(state, name) {
// careful: the Valid method is skipped, make sure this is always valid
op := &BindMountOp{
Source: r.Host.Append(name),
@@ -78,7 +78,7 @@ func (r *AutoRootOp) String() string {
}
// IsAutoRootBindable returns whether a dir entry name is selected for AutoRoot.
-func IsAutoRootBindable(name string) bool {
+func IsAutoRootBindable(msg Msg, name string) bool {
switch name {
case "proc", "dev", "tmp", "mnt", "etc":
diff --git a/container/autoroot_test.go b/container/autoroot_test.go
index 3c904312..fe662226 100644
--- a/container/autoroot_test.go
+++ b/container/autoroot_test.go
@@ -180,19 +180,26 @@ func TestIsAutoRootBindable(t *testing.T) {
testCases := []struct {
name string
want bool
+ log bool
}{
- {"proc", false},
- {"dev", false},
- {"tmp", false},
- {"mnt", false},
- {"etc", false},
- {"", false},
+ {"proc", false, false},
+ {"dev", false, false},
+ {"tmp", false, false},
+ {"mnt", false, false},
+ {"etc", false, false},
+ {"", false, true},
- {"var", true},
+ {"var", true, false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- if got := IsAutoRootBindable(tc.name); got != tc.want {
+ var msg Msg
+ if tc.log {
+ msg = &kstub{nil, stub.New(t, func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { panic("unreachable") }, stub.Expect{Calls: []stub.Call{
+ call("verbose", stub.ExpectArgs{[]any{"got unexpected root entry"}}, nil, nil),
+ }})}
+ }
+ if got := IsAutoRootBindable(msg, tc.name); got != tc.want {
t.Errorf("IsAutoRootBindable: %v, want %v", got, tc.want)
}
})
diff --git a/container/container.go b/container/container.go
index f49faa89..c19c2e7f 100644
--- a/container/container.go
+++ b/container/container.go
@@ -49,6 +49,7 @@ type (
cmd *exec.Cmd
ctx context.Context
+ msg Msg
Params
}
@@ -162,10 +163,10 @@ func (p *Container) Start() error {
// map to overflow id to work around ownership checks
if p.Uid < 1 {
- p.Uid = OverflowUid()
+ p.Uid = OverflowUid(p.msg)
}
if p.Gid < 1 {
- p.Gid = OverflowGid()
+ p.Gid = OverflowGid(p.msg)
}
if !p.RetainSession {
@@ -263,19 +264,19 @@ func (p *Container) Start() error {
}
return &StartError{false, "kernel version too old for LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", ENOSYS, true, false}
} else {
- msg.Verbosef("landlock abi version %d", abi)
+ p.msg.Verbosef("landlock abi version %d", abi)
}
if rulesetFd, err := rulesetAttr.Create(0); err != nil {
return &StartError{true, "create landlock ruleset", err, false, false}
} else {
- msg.Verbosef("enforcing landlock ruleset %s", rulesetAttr)
+ p.msg.Verbosef("enforcing landlock ruleset %s", rulesetAttr)
if err = LandlockRestrictSelf(rulesetFd, 0); err != nil {
_ = Close(rulesetFd)
return &StartError{true, "enforce landlock ruleset", err, false, false}
}
if err = Close(rulesetFd); err != nil {
- msg.Verbosef("cannot close landlock ruleset: %v", err)
+ p.msg.Verbosef("cannot close landlock ruleset: %v", err)
// not fatal
}
}
@@ -283,7 +284,7 @@ func (p *Container) Start() error {
landlockOut:
}
- msg.Verbose("starting container init")
+ p.msg.Verbose("starting container init")
if err := p.cmd.Start(); err != nil {
return &StartError{false, "start container init", err, false, true}
}
@@ -325,7 +326,7 @@ func (p *Container) Serve() error {
Getuid(),
Getgid(),
len(p.ExtraFiles),
- msg.IsVerbose(),
+ p.msg.IsVerbose(),
},
)
if err != nil {
@@ -392,17 +393,21 @@ func (p *Container) ProcessState() *os.ProcessState {
}
// New returns the address to a new instance of [Container] that requires further initialisation before use.
-func New(ctx context.Context) *Container {
- p := &Container{ctx: ctx, Params: Params{Ops: new(Ops)}}
+func New(ctx context.Context, msg Msg) *Container {
+ if msg == nil {
+ msg = NewMsg(nil)
+ }
+
+ p := &Container{ctx: ctx, msg: msg, Params: Params{Ops: new(Ops)}}
c, cancel := context.WithCancel(ctx)
p.cancel = cancel
- p.cmd = exec.CommandContext(c, MustExecutable())
+ p.cmd = exec.CommandContext(c, MustExecutable(msg))
return p
}
// NewCommand calls [New] and initialises the [Params.Path] and [Params.Args] fields.
-func NewCommand(ctx context.Context, pathname *Absolute, name string, args ...string) *Container {
- z := New(ctx)
+func NewCommand(ctx context.Context, msg Msg, pathname *Absolute, name string, args ...string) *Container {
+ z := New(ctx, msg)
z.Path = pathname
z.Args = append([]string{name}, args...)
return z
diff --git a/container/container_test.go b/container/container_test.go
index 0b6cdf51..22356484 100644
--- a/container/container_test.go
+++ b/container/container_test.go
@@ -23,8 +23,6 @@ import (
"hakurei.app/container/seccomp"
"hakurei.app/container/vfs"
"hakurei.app/hst"
- "hakurei.app/internal"
- "hakurei.app/internal/hlog"
"hakurei.app/ldd"
)
@@ -351,8 +349,6 @@ var containerTestCases = []struct {
}
func TestContainer(t *testing.T) {
- replaceOutput(t)
-
t.Run("cancel", testContainerCancel(nil, func(t *testing.T, c *container.Container) {
wantErr := context.Canceled
wantExitCode := 0
@@ -547,7 +543,8 @@ func testContainerCancel(
}
func TestContainerString(t *testing.T) {
- c := container.NewCommand(t.Context(), container.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env")
+ msg := container.NewMsg(nil)
+ c := container.NewCommand(t.Context(), msg, container.MustAbs("/run/current-system/sw/bin/ldd"), "ldd", "/usr/bin/env")
c.SeccompFlags |= seccomp.AllowMultiarch
c.SeccompRules = seccomp.Preset(
seccomp.PresetExt|seccomp.PresetDenyNS|seccomp.PresetDenyTTY,
@@ -689,7 +686,7 @@ var (
var helperCommands []func(c command.Command)
func TestMain(m *testing.M) {
- container.TryArgv0(hlog.Output{}, hlog.Prepare, internal.InstallOutput)
+ container.TryArgv0(nil)
if os.Getenv(envDoCheck) == "1" {
c := command.New(os.Stderr, log.Printf, "helper", func(args []string) error {
@@ -712,12 +709,13 @@ func TestMain(m *testing.M) {
}
func helperNewContainerLibPaths(ctx context.Context, libPaths *[]*container.Absolute, args ...string) (c *container.Container) {
- c = container.NewCommand(ctx, absHelperInnerPath, "helper", args...)
+ msg := container.NewMsg(nil)
+ c = container.NewCommand(ctx, msg, absHelperInnerPath, "helper", args...)
c.Env = append(c.Env, envDoCheck+"=1")
c.Bind(container.MustAbs(os.Args[0]), absHelperInnerPath, 0)
// in case test has cgo enabled
- if entries, err := ldd.Exec(ctx, os.Args[0]); err != nil {
+ if entries, err := ldd.Exec(ctx, msg, os.Args[0]); err != nil {
log.Fatalf("ldd: %v", err)
} else {
*libPaths = ldd.Path(entries)
diff --git a/container/dispatcher.go b/container/dispatcher.go
index 929421bd..65881cc7 100644
--- a/container/dispatcher.go
+++ b/container/dispatcher.go
@@ -3,7 +3,6 @@ package container
import (
"io"
"io/fs"
- "log"
"os"
"os/exec"
"os/signal"
@@ -38,7 +37,7 @@ type syscallDispatcher interface {
setNoNewPrivs() error
// lastcap provides [LastCap].
- lastcap() uintptr
+ lastcap(msg Msg) uintptr
// capset provides capset.
capset(hdrp *capHeader, datap *[2]capData) error
// capBoundingSetDrop provides capBoundingSetDrop.
@@ -53,9 +52,9 @@ type syscallDispatcher interface {
receive(key string, e any, fdp *uintptr) (closeFunc func() error, err error)
// bindMount provides procPaths.bindMount.
- bindMount(source, target string, flags uintptr) error
+ bindMount(msg Msg, source, target string, flags uintptr) error
// remount provides procPaths.remount.
- remount(target string, flags uintptr) error
+ remount(msg Msg, target string, flags uintptr) error
// mountTmpfs provides mountTmpfs.
mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error
// ensureFile provides ensureFile.
@@ -122,22 +121,12 @@ type syscallDispatcher interface {
// wait4 provides syscall.Wait4
wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error)
- // printf provides [log.Printf].
- printf(format string, v ...any)
- // fatal provides [log.Fatal]
- fatal(v ...any)
- // fatalf provides [log.Fatalf]
- fatalf(format string, v ...any)
- // verbose provides [Msg.Verbose].
- verbose(v ...any)
- // verbosef provides [Msg.Verbosef].
- verbosef(format string, v ...any)
- // suspend provides [Msg.Suspend].
- suspend()
- // resume provides [Msg.Resume].
- resume() bool
- // beforeExit provides [Msg.BeforeExit].
- beforeExit()
+ // printf provides the Printf method of [log.Logger].
+ printf(msg Msg, format string, v ...any)
+ // fatal provides the Fatal method of [log.Logger]
+ fatal(msg Msg, v ...any)
+ // fatalf provides the Fatalf method of [log.Logger]
+ fatalf(msg Msg, format string, v ...any)
}
// direct implements syscallDispatcher on the current kernel.
@@ -151,7 +140,7 @@ func (direct) setPtracer(pid uintptr) error { return SetPtracer(pid) }
func (direct) setDumpable(dumpable uintptr) error { return SetDumpable(dumpable) }
func (direct) setNoNewPrivs() error { return SetNoNewPrivs() }
-func (direct) lastcap() uintptr { return LastCap() }
+func (direct) lastcap(msg Msg) uintptr { return LastCap(msg) }
func (direct) capset(hdrp *capHeader, datap *[2]capData) error { return capset(hdrp, datap) }
func (direct) capBoundingSetDrop(cap uintptr) error { return capBoundingSetDrop(cap) }
func (direct) capAmbientClearAll() error { return capAmbientClearAll() }
@@ -161,11 +150,11 @@ func (direct) receive(key string, e any, fdp *uintptr) (func() error, error) {
return Receive(key, e, fdp)
}
-func (direct) bindMount(source, target string, flags uintptr) error {
- return hostProc.bindMount(source, target, flags)
+func (direct) bindMount(msg Msg, source, target string, flags uintptr) error {
+ return hostProc.bindMount(msg, source, target, flags)
}
-func (direct) remount(target string, flags uintptr) error {
- return hostProc.remount(target, flags)
+func (direct) remount(msg Msg, target string, flags uintptr) error {
+ return hostProc.remount(msg, target, flags)
}
func (k direct) mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error {
return mountTmpfs(k, fsname, target, flags, size, perm)
@@ -232,11 +221,6 @@ func (direct) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *s
return syscall.Wait4(pid, wstatus, options, rusage)
}
-func (direct) printf(format string, v ...any) { log.Printf(format, v...) }
-func (direct) fatal(v ...any) { log.Fatal(v...) }
-func (direct) fatalf(format string, v ...any) { log.Fatalf(format, v...) }
-func (direct) verbose(v ...any) { msg.Verbose(v...) }
-func (direct) verbosef(format string, v ...any) { msg.Verbosef(format, v...) }
-func (direct) suspend() { msg.Suspend() }
-func (direct) resume() bool { return msg.Resume() }
-func (direct) beforeExit() { msg.BeforeExit() }
+func (direct) printf(msg Msg, format string, v ...any) { msg.GetLogger().Printf(format, v...) }
+func (direct) fatal(msg Msg, v ...any) { msg.GetLogger().Fatal(v...) }
+func (direct) fatalf(msg Msg, format string, v ...any) { msg.GetLogger().Fatalf(format, v...) }
diff --git a/container/dispatcher_test.go b/container/dispatcher_test.go
index 7e141304..2a2685fd 100644
--- a/container/dispatcher_test.go
+++ b/container/dispatcher_test.go
@@ -2,8 +2,10 @@ package container
import (
"bytes"
+ "fmt"
"io"
"io/fs"
+ "log"
"os"
"os/exec"
"reflect"
@@ -136,7 +138,7 @@ func call(name string, args stub.ExpectArgs, ret any, err error) stub.Call {
type simpleTestCase struct {
name string
- f func(k syscallDispatcher) error
+ f func(k *kstub) error
want stub.Expect
wantErr error
}
@@ -185,11 +187,11 @@ func checkOpBehaviour(t *testing.T, testCases []opBehaviourTestCase) {
t.Run(tc.name, func(t *testing.T) {
t.Helper()
- state := &setupState{Params: tc.params}
k := &kstub{nil, stub.New(t,
func(s *stub.Stub[syscallDispatcher]) syscallDispatcher { return &kstub{nil, s} },
stub.Expect{Calls: slices.Concat(tc.early, []stub.Call{{Name: stub.CallSeparator}}, tc.apply)},
)}
+ state := &setupState{Params: tc.params, Msg: k}
defer stub.HandleExit(t)
errEarly := tc.op.early(state, k)
k.Expects(stub.CallSeparator)
@@ -327,7 +329,11 @@ func (k *kstub) setDumpable(dumpable uintptr) error {
}
func (k *kstub) setNoNewPrivs() error { k.Helper(); return k.Expects("setNoNewPrivs").Err }
-func (k *kstub) lastcap() uintptr { k.Helper(); return k.Expects("lastcap").Ret.(uintptr) }
+func (k *kstub) lastcap(msg Msg) uintptr {
+ k.Helper()
+ k.checkMsg(msg)
+ return k.Expects("lastcap").Ret.(uintptr)
+}
func (k *kstub) capset(hdrp *capHeader, datap *[2]capData) error {
k.Helper()
@@ -403,16 +409,18 @@ func (k *kstub) receive(key string, e any, fdp *uintptr) (closeFunc func() error
return
}
-func (k *kstub) bindMount(source, target string, flags uintptr) error {
+func (k *kstub) bindMount(msg Msg, source, target string, flags uintptr) error {
k.Helper()
+ k.checkMsg(msg)
return k.Expects("bindMount").Error(
stub.CheckArg(k.Stub, "source", source, 0),
stub.CheckArg(k.Stub, "target", target, 1),
stub.CheckArg(k.Stub, "flags", flags, 2))
}
-func (k *kstub) remount(target string, flags uintptr) error {
+func (k *kstub) remount(msg Msg, target string, flags uintptr) error {
k.Helper()
+ k.checkMsg(msg)
return k.Expects("remount").Error(
stub.CheckArg(k.Stub, "target", target, 0),
stub.CheckArg(k.Stub, "flags", flags, 1))
@@ -694,7 +702,7 @@ func (k *kstub) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage
return
}
-func (k *kstub) printf(format string, v ...any) {
+func (k *kstub) printf(_ Msg, format string, v ...any) {
k.Helper()
if k.Expects("printf").Error(
stub.CheckArg(k.Stub, "format", format, 0),
@@ -703,7 +711,7 @@ func (k *kstub) printf(format string, v ...any) {
}
}
-func (k *kstub) fatal(v ...any) {
+func (k *kstub) fatal(_ Msg, v ...any) {
k.Helper()
if k.Expects("fatal").Error(
stub.CheckArgReflect(k.Stub, "v", v, 0)) != nil {
@@ -712,7 +720,7 @@ func (k *kstub) fatal(v ...any) {
panic(stub.PanicExit)
}
-func (k *kstub) fatalf(format string, v ...any) {
+func (k *kstub) fatalf(_ Msg, format string, v ...any) {
k.Helper()
if k.Expects("fatalf").Error(
stub.CheckArg(k.Stub, "format", format, 0),
@@ -722,7 +730,35 @@ func (k *kstub) fatalf(format string, v ...any) {
panic(stub.PanicExit)
}
-func (k *kstub) verbose(v ...any) {
+func (k *kstub) checkMsg(msg Msg) {
+ k.Helper()
+ var target *kstub
+
+ if state, ok := msg.(*setupState); ok {
+ target = state.Msg.(*kstub)
+ } else {
+ target = msg.(*kstub)
+ }
+
+ if k != target {
+ panic(fmt.Sprintf("unexpected Msg: %#v", msg))
+ }
+}
+
+func (k *kstub) GetLogger() *log.Logger { panic("unreachable") }
+func (k *kstub) IsVerbose() bool { panic("unreachable") }
+
+func (k *kstub) SwapVerbose(verbose bool) bool {
+ k.Helper()
+ expect := k.Expects("swapVerbose")
+ if expect.Error(
+ stub.CheckArg(k.Stub, "verbose", verbose, 0)) != nil {
+ k.FailNow()
+ }
+ return expect.Ret.(bool)
+}
+
+func (k *kstub) Verbose(v ...any) {
k.Helper()
if k.Expects("verbose").Error(
stub.CheckArgReflect(k.Stub, "v", v, 0)) != nil {
@@ -730,7 +766,7 @@ func (k *kstub) verbose(v ...any) {
}
}
-func (k *kstub) verbosef(format string, v ...any) {
+func (k *kstub) Verbosef(format string, v ...any) {
k.Helper()
if k.Expects("verbosef").Error(
stub.CheckArg(k.Stub, "format", format, 0),
@@ -739,6 +775,6 @@ func (k *kstub) verbosef(format string, v ...any) {
}
}
-func (k *kstub) suspend() { k.Helper(); k.Expects("suspend") }
-func (k *kstub) resume() bool { k.Helper(); return k.Expects("resume").Ret.(bool) }
-func (k *kstub) beforeExit() { k.Helper(); k.Expects("beforeExit") }
+func (k *kstub) Suspend() bool { k.Helper(); return k.Expects("suspend").Ret.(bool) }
+func (k *kstub) Resume() bool { k.Helper(); return k.Expects("resume").Ret.(bool) }
+func (k *kstub) BeforeExit() { k.Helper(); k.Expects("beforeExit") }
diff --git a/container/executable.go b/container/executable.go
index d19d56f5..9b6d4f66 100644
--- a/container/executable.go
+++ b/container/executable.go
@@ -1,7 +1,6 @@
package container
import (
- "log"
"os"
"sync"
)
@@ -11,16 +10,16 @@ var (
executableOnce sync.Once
)
-func copyExecutable() {
+func copyExecutable(msg Msg) {
if name, err := os.Executable(); err != nil {
msg.BeforeExit()
- log.Fatalf("cannot read executable path: %v", err)
+ msg.GetLogger().Fatalf("cannot read executable path: %v", err)
} else {
executable = name
}
}
-func MustExecutable() string {
- executableOnce.Do(copyExecutable)
+func MustExecutable(msg Msg) string {
+ executableOnce.Do(func() { copyExecutable(msg) })
return executable
}
diff --git a/container/executable_test.go b/container/executable_test.go
index 2a44017a..5608a8ae 100644
--- a/container/executable_test.go
+++ b/container/executable_test.go
@@ -9,7 +9,7 @@ import (
func TestExecutable(t *testing.T) {
for i := 0; i < 16; i++ {
- if got := container.MustExecutable(); got != os.Args[0] {
+ if got := container.MustExecutable(container.NewMsg(nil)); got != os.Args[0] {
t.Errorf("MustExecutable: %q, want %q",
got, os.Args[0])
}
diff --git a/container/init.go b/container/init.go
index 73478b5a..e65472e7 100644
--- a/container/init.go
+++ b/container/init.go
@@ -3,6 +3,7 @@ package container
import (
"errors"
"fmt"
+ "log"
"os"
"os/exec"
"path"
@@ -59,6 +60,7 @@ type (
setupState struct {
nonrepeatable uintptr
*Params
+ Msg
}
)
@@ -91,20 +93,23 @@ type initParams struct {
Verbose bool
}
-func Init(prepareLogger func(prefix string), setVerbose func(verbose bool)) {
- initEntrypoint(direct{}, prepareLogger, setVerbose)
+// Init is called by [TryArgv0] if the current process is the container init.
+func Init(msg Msg) {
+ if msg == nil {
+ panic("attempting to call initEntrypoint with nil msg")
+ }
+ initEntrypoint(direct{}, msg)
}
-func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setVerbose func(verbose bool)) {
+func initEntrypoint(k syscallDispatcher, msg Msg) {
k.lockOSThread()
- prepareLogger("init")
if k.getpid() != 1 {
- k.fatal("this process must run as pid 1")
+ k.fatal(msg, "this process must run as pid 1")
}
if err := k.setPtracer(0); err != nil {
- k.verbosef("cannot enable ptrace protection via Yama LSM: %v", err)
+ msg.Verbosef("cannot enable ptrace protection via Yama LSM: %v", err)
// not fatal: this program has no additional privileges at initial program start
}
@@ -116,65 +121,65 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
)
if f, err := k.receive(setupEnv, &params, &setupFd); err != nil {
if errors.Is(err, EBADF) {
- k.fatal("invalid setup descriptor")
+ k.fatal(msg, "invalid setup descriptor")
}
if errors.Is(err, ErrReceiveEnv) {
- k.fatal("HAKUREI_SETUP not set")
+ k.fatal(msg, "HAKUREI_SETUP not set")
}
- k.fatalf("cannot decode init setup payload: %v", err)
+ k.fatalf(msg, "cannot decode init setup payload: %v", err)
} else {
if params.Ops == nil {
- k.fatal("invalid setup parameters")
+ k.fatal(msg, "invalid setup parameters")
}
if params.ParentPerm == 0 {
params.ParentPerm = 0755
}
- setVerbose(params.Verbose)
- k.verbose("received setup parameters")
+ msg.SwapVerbose(params.Verbose)
+ msg.Verbose("received setup parameters")
closeSetup = f
offsetSetup = int(setupFd + 1)
}
// write uid/gid map here so parent does not need to set dumpable
if err := k.setDumpable(SUID_DUMP_USER); err != nil {
- k.fatalf("cannot set SUID_DUMP_USER: %v", err)
+ k.fatalf(msg, "cannot set SUID_DUMP_USER: %v", err)
}
if err := k.writeFile(FHSProc+"self/uid_map",
append([]byte{}, strconv.Itoa(params.Uid)+" "+strconv.Itoa(params.HostUid)+" 1\n"...),
0); err != nil {
- k.fatalf("%v", err)
+ k.fatalf(msg, "%v", err)
}
if err := k.writeFile(FHSProc+"self/setgroups",
[]byte("deny\n"),
0); err != nil && !os.IsNotExist(err) {
- k.fatalf("%v", err)
+ k.fatalf(msg, "%v", err)
}
if err := k.writeFile(FHSProc+"self/gid_map",
append([]byte{}, strconv.Itoa(params.Gid)+" "+strconv.Itoa(params.HostGid)+" 1\n"...),
0); err != nil {
- k.fatalf("%v", err)
+ k.fatalf(msg, "%v", err)
}
if err := k.setDumpable(SUID_DUMP_DISABLE); err != nil {
- k.fatalf("cannot set SUID_DUMP_DISABLE: %v", err)
+ k.fatalf(msg, "cannot set SUID_DUMP_DISABLE: %v", err)
}
oldmask := k.umask(0)
if params.Hostname != "" {
if err := k.sethostname([]byte(params.Hostname)); err != nil {
- k.fatalf("cannot set hostname: %v", err)
+ k.fatalf(msg, "cannot set hostname: %v", err)
}
}
// cache sysctl before pivot_root
- lastcap := k.lastcap()
+ lastcap := k.lastcap(msg)
if err := k.mount(zeroString, FHSRoot, zeroString, MS_SILENT|MS_SLAVE|MS_REC, zeroString); err != nil {
- k.fatalf("cannot make / rslave: %v", err)
+ k.fatalf(msg, "cannot make / rslave: %v", err)
}
- state := &setupState{Params: &params.Params}
+ state := &setupState{Params: &params.Params, Msg: msg}
/* early is called right before pivot_root into intermediate root;
this step is mostly for gathering information that would otherwise be difficult to obtain
@@ -182,41 +187,41 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
the state of the mount namespace */
for i, op := range *params.Ops {
if op == nil || !op.Valid() {
- k.fatalf("invalid op at index %d", i)
+ k.fatalf(msg, "invalid op at index %d", i)
}
if err := op.early(state, k); err != nil {
if m, ok := messageFromError(err); ok {
- k.fatal(m)
+ k.fatal(msg, m)
} else {
- k.fatalf("cannot prepare op at index %d: %v", i, err)
+ k.fatalf(msg, "cannot prepare op at index %d: %v", i, err)
}
}
}
if err := k.mount(SourceTmpfsRootfs, intermediateHostPath, FstypeTmpfs, MS_NODEV|MS_NOSUID, zeroString); err != nil {
- k.fatalf("cannot mount intermediate root: %v", err)
+ k.fatalf(msg, "cannot mount intermediate root: %v", err)
}
if err := k.chdir(intermediateHostPath); err != nil {
- k.fatalf("cannot enter intermediate host path: %v", err)
+ k.fatalf(msg, "cannot enter intermediate host path: %v", err)
}
if err := k.mkdir(sysrootDir, 0755); err != nil {
- k.fatalf("%v", err)
+ k.fatalf(msg, "%v", err)
}
if err := k.mount(sysrootDir, sysrootDir, zeroString, MS_SILENT|MS_BIND|MS_REC, zeroString); err != nil {
- k.fatalf("cannot bind sysroot: %v", err)
+ k.fatalf(msg, "cannot bind sysroot: %v", err)
}
if err := k.mkdir(hostDir, 0755); err != nil {
- k.fatalf("%v", err)
+ k.fatalf(msg, "%v", err)
}
// pivot_root uncovers intermediateHostPath in hostDir
if err := k.pivotRoot(intermediateHostPath, hostDir); err != nil {
- k.fatalf("cannot pivot into intermediate root: %v", err)
+ k.fatalf(msg, "cannot pivot into intermediate root: %v", err)
}
if err := k.chdir(FHSRoot); err != nil {
- k.fatalf("cannot enter intermediate root: %v", err)
+ k.fatalf(msg, "cannot enter intermediate root: %v", err)
}
/* apply is called right after pivot_root and entering the new root;
@@ -226,23 +231,23 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
for i, op := range *params.Ops {
// ops already checked during early setup
if prefix, ok := op.prefix(); ok {
- k.verbosef("%s %s", prefix, op)
+ msg.Verbosef("%s %s", prefix, op)
}
if err := op.apply(state, k); err != nil {
if m, ok := messageFromError(err); ok {
- k.fatal(m)
+ k.fatal(msg, m)
} else {
- k.fatalf("cannot apply op at index %d: %v", i, err)
+ k.fatalf(msg, "cannot apply op at index %d: %v", i, err)
}
}
}
// setup requiring host root complete at this point
if err := k.mount(hostDir, hostDir, zeroString, MS_SILENT|MS_REC|MS_PRIVATE, zeroString); err != nil {
- k.fatalf("cannot make host root rprivate: %v", err)
+ k.fatalf(msg, "cannot make host root rprivate: %v", err)
}
if err := k.unmount(hostDir, MNT_DETACH); err != nil {
- k.fatalf("cannot unmount host root: %v", err)
+ k.fatalf(msg, "cannot unmount host root: %v", err)
}
{
@@ -251,39 +256,39 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
fd, err = k.open(FHSRoot, O_DIRECTORY|O_RDONLY, 0)
return
}); err != nil {
- k.fatalf("cannot open intermediate root: %v", err)
+ k.fatalf(msg, "cannot open intermediate root: %v", err)
}
if err := k.chdir(sysrootPath); err != nil {
- k.fatalf("cannot enter sysroot: %v", err)
+ k.fatalf(msg, "cannot enter sysroot: %v", err)
}
if err := k.pivotRoot(".", "."); err != nil {
- k.fatalf("cannot pivot into sysroot: %v", err)
+ k.fatalf(msg, "cannot pivot into sysroot: %v", err)
}
if err := k.fchdir(fd); err != nil {
- k.fatalf("cannot re-enter intermediate root: %v", err)
+ k.fatalf(msg, "cannot re-enter intermediate root: %v", err)
}
if err := k.unmount(".", MNT_DETACH); err != nil {
- k.fatalf("cannot unmount intermediate root: %v", err)
+ k.fatalf(msg, "cannot unmount intermediate root: %v", err)
}
if err := k.chdir(FHSRoot); err != nil {
- k.fatalf("cannot enter root: %v", err)
+ k.fatalf(msg, "cannot enter root: %v", err)
}
if err := k.close(fd); err != nil {
- k.fatalf("cannot close intermediate root: %v", err)
+ k.fatalf(msg, "cannot close intermediate root: %v", err)
}
}
if err := k.capAmbientClearAll(); err != nil {
- k.fatalf("cannot clear the ambient capability set: %v", err)
+ k.fatalf(msg, "cannot clear the ambient capability set: %v", err)
}
for i := uintptr(0); i <= lastcap; i++ {
if params.Privileged && i == CAP_SYS_ADMIN {
continue
}
if err := k.capBoundingSetDrop(i); err != nil {
- k.fatalf("cannot drop capability from bounding set: %v", err)
+ k.fatalf(msg, "cannot drop capability from bounding set: %v", err)
}
}
@@ -292,29 +297,29 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
keep[capToIndex(CAP_SYS_ADMIN)] |= capToMask(CAP_SYS_ADMIN)
if err := k.capAmbientRaise(CAP_SYS_ADMIN); err != nil {
- k.fatalf("cannot raise CAP_SYS_ADMIN: %v", err)
+ k.fatalf(msg, "cannot raise CAP_SYS_ADMIN: %v", err)
}
}
if err := k.capset(
&capHeader{_LINUX_CAPABILITY_VERSION_3, 0},
&[2]capData{{0, keep[0], keep[0]}, {0, keep[1], keep[1]}},
); err != nil {
- k.fatalf("cannot capset: %v", err)
+ k.fatalf(msg, "cannot capset: %v", err)
}
if !params.SeccompDisable {
rules := params.SeccompRules
if len(rules) == 0 { // non-empty rules slice always overrides presets
- k.verbosef("resolving presets %#x", params.SeccompPresets)
+ msg.Verbosef("resolving presets %#x", params.SeccompPresets)
rules = seccomp.Preset(params.SeccompPresets, params.SeccompFlags)
}
if err := k.seccompLoad(rules, params.SeccompFlags); err != nil {
// this also indirectly asserts PR_SET_NO_NEW_PRIVS
- k.fatalf("cannot load syscall filter: %v", err)
+ k.fatalf(msg, "cannot load syscall filter: %v", err)
}
- k.verbosef("%d filter rules loaded", len(rules))
+ msg.Verbosef("%d filter rules loaded", len(rules))
} else {
- k.verbose("syscall filter not configured")
+ msg.Verbose("syscall filter not configured")
}
extraFiles := make([]*os.File, params.Count)
@@ -331,14 +336,14 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
cmd.ExtraFiles = extraFiles
cmd.Dir = params.Dir.String()
- k.verbosef("starting initial program %s", params.Path)
+ msg.Verbosef("starting initial program %s", params.Path)
if err := k.start(cmd); err != nil {
- k.fatalf("%v", err)
+ k.fatalf(msg, "%v", err)
}
- k.suspend()
+ msg.Suspend()
if err := closeSetup(); err != nil {
- k.printf("cannot close setup pipe: %v", err)
+ k.printf(msg, "cannot close setup pipe: %v", err)
// not fatal
}
@@ -372,7 +377,7 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
}
}
if !errors.Is(err, ECHILD) {
- k.printf("unexpected wait4 response: %v", err)
+ k.printf(msg, "unexpected wait4 response: %v", err)
}
close(done)
@@ -389,50 +394,50 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
for {
select {
case s := <-sig:
- if k.resume() {
- k.verbosef("%s after process start", s.String())
+ if msg.Resume() {
+ msg.Verbosef("%s after process start", s.String())
} else {
- k.verbosef("got %s", s.String())
+ msg.Verbosef("got %s", s.String())
}
if s == CancelSignal && params.ForwardCancel && cmd.Process != nil {
- k.verbose("forwarding context cancellation")
+ msg.Verbose("forwarding context cancellation")
if err := k.signal(cmd, os.Interrupt); err != nil {
- k.printf("cannot forward cancellation: %v", err)
+ k.printf(msg, "cannot forward cancellation: %v", err)
}
continue
}
- k.beforeExit()
+ msg.BeforeExit()
k.exit(0)
case w := <-info:
if w.wpid == cmd.Process.Pid {
// initial process exited, output is most likely available again
- k.resume()
+ msg.Resume()
switch {
case w.wstatus.Exited():
r = w.wstatus.ExitStatus()
- k.verbosef("initial process exited with code %d", w.wstatus.ExitStatus())
+ msg.Verbosef("initial process exited with code %d", w.wstatus.ExitStatus())
case w.wstatus.Signaled():
r = 128 + int(w.wstatus.Signal())
- k.verbosef("initial process exited with signal %s", w.wstatus.Signal())
+ msg.Verbosef("initial process exited with signal %s", w.wstatus.Signal())
default:
r = 255
- k.verbosef("initial process exited with status %#x", w.wstatus)
+ msg.Verbosef("initial process exited with status %#x", w.wstatus)
}
go func() { time.Sleep(params.AdoptWaitDelay); close(timeout) }()
}
case <-done:
- k.beforeExit()
+ msg.BeforeExit()
k.exit(r)
case <-timeout:
- k.printf("timeout exceeded waiting for lingering processes")
- k.beforeExit()
+ k.printf(msg, "timeout exceeded waiting for lingering processes")
+ msg.BeforeExit()
k.exit(r)
}
}
@@ -441,10 +446,16 @@ func initEntrypoint(k syscallDispatcher, prepareLogger func(prefix string), setV
const initName = "init"
// TryArgv0 calls [Init] if the last element of argv0 is "init".
-func TryArgv0(v Msg, prepare func(prefix string), setVerbose func(verbose bool)) {
+// If a nil msg is passed, the system logger is used instead.
+func TryArgv0(msg Msg) {
+ if msg == nil {
+ log.SetPrefix(initName + ": ")
+ log.SetFlags(0)
+ msg = NewMsg(log.Default())
+ }
+
if len(os.Args) > 0 && path.Base(os.Args[0]) == initName {
- msg = v
- Init(prepare, setVerbose)
+ Init(msg)
msg.BeforeExit()
os.Exit(0)
}
diff --git a/container/init_test.go b/container/init_test.go
index 7dd92c09..162afad7 100644
--- a/container/init_test.go
+++ b/container/init_test.go
@@ -11,25 +11,9 @@ import (
)
func TestInitEntrypoint(t *testing.T) {
- assertPrefix := func(prefix string) {
- if prefix != "init" {
- t.Fatalf("prepareLogger: prefix = %q", prefix)
- }
- }
-
- assertVerboseFalse := func(verbose bool) {
- if verbose {
- t.Fatal("setVerbose: verbose = true, want false")
- }
- }
- assertVerboseTrue := func(verbose bool) {
- if !verbose {
- t.Fatal("setVerbose: verbose = false, want true")
- }
- }
checkSimple(t, "initEntrypoint", []simpleTestCase{
- {"getpid", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"getpid", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1<<10, nil),
@@ -37,7 +21,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"receive bad fd", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"receive bad fd", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -47,7 +31,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"receive not set", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"receive not set", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -57,7 +41,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"receive payload decode", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"receive payload decode", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -67,7 +51,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"receive invalid params", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"receive invalid params", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -91,7 +75,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"setDumpable user", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"setDumpable user", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -112,13 +96,14 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(78), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, stub.UniqueError(77)),
call("fatalf", stub.ExpectArgs{"cannot set SUID_DUMP_USER: %v", []any{stub.UniqueError(77)}}, nil, nil),
},
}, nil},
- {"writeFile uid_map", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"writeFile uid_map", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -139,6 +124,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(76), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, stub.UniqueError(75)),
@@ -146,7 +132,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"writeFile setgroups", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"writeFile setgroups", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -167,6 +153,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(74), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -175,7 +162,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"writeFile gid_map", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"writeFile gid_map", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -196,6 +183,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(72), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -205,7 +193,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"setDumpable disable", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"setDumpable disable", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -226,6 +214,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(70), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -236,7 +225,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"sethostname", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"sethostname", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -257,6 +246,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(68), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -269,7 +259,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"mount rslave root", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"mount rslave root", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -290,6 +280,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(66), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -304,7 +295,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"nil op", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"nil op", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -325,6 +316,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(64), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -341,7 +333,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"invalid op", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"invalid op", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -362,6 +354,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(63), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -378,7 +371,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"early unhandled error", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"early unhandled error", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -399,6 +392,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(62), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -416,7 +410,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"early", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"early", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -437,6 +431,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(60), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -454,7 +449,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"mount ih", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"mount ih", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -475,6 +470,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(59), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -493,7 +489,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"chdir ih", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"chdir ih", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -514,6 +510,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(57), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -533,7 +530,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"mkdir sysroot", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"mkdir sysroot", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -554,6 +551,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(55), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -574,7 +572,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"mount bind sysroot", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"mount bind sysroot", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -595,6 +593,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(53), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -616,7 +615,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"mkdir host", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"mkdir host", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -637,6 +636,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(51), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -659,7 +659,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"pivotRoot ir", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"pivotRoot ir", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -680,6 +680,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(49), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -703,7 +704,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"chdir ir", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"chdir ir", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -724,6 +725,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(47), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -748,7 +750,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"apply unhandled error", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"apply unhandled error", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -769,6 +771,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(45), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -802,7 +805,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"apply", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"apply", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -823,6 +826,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(43), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -856,7 +860,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"mount rprivate host", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"mount rprivate host", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -877,6 +881,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(42), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -911,7 +916,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"unmount host", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"unmount host", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -932,6 +937,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(40), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -967,7 +973,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"open ir", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"open ir", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -988,6 +994,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(38), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1025,7 +1032,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"chdir sysroot", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"chdir sysroot", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1046,6 +1053,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(36), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1084,7 +1092,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"pivotRoot sysroot", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"pivotRoot sysroot", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1105,6 +1113,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(34), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1144,7 +1153,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"fchdir ir", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"fchdir ir", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1165,6 +1174,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(32), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1205,7 +1215,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"unmount ir", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"unmount ir", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1226,6 +1236,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(30), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1267,7 +1278,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"chdir ir", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"chdir ir", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1288,6 +1299,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(28), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1330,7 +1342,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"close ir", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"close ir", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1351,6 +1363,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(26), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1394,7 +1407,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"capAmbientClearAll", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"capAmbientClearAll", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1415,6 +1428,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(24), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1459,7 +1473,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"capBoundingSetDrop", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"capBoundingSetDrop", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1480,6 +1494,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(22), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1532,7 +1547,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"capAmbientRaise", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"capAmbientRaise", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1553,6 +1568,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(20), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1638,7 +1654,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"capset", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"capset", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1659,6 +1675,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(18), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1745,7 +1762,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"seccompLoad", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"seccompLoad", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1766,6 +1783,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(16), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -1854,7 +1872,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"start", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseFalse); return nil }, stub.Expect{
+ {"start", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
call("getpid", stub.ExpectArgs{}, 1, nil),
@@ -1874,6 +1892,7 @@ func TestInitEntrypoint(t *testing.T) {
SeccompDisable: true,
ParentPerm: 0750,
}, 1971, 127, 2, false}, uintptr(0x39)}, stub.UniqueError(13), nil),
+ call("swapVerbose", stub.ExpectArgs{false}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("16777216 1971 1\n"), os.FileMode(0)}, nil, nil),
@@ -1966,7 +1985,7 @@ func TestInitEntrypoint(t *testing.T) {
},
}, nil},
- {"lowlastcap signaled cancel forward error", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseFalse); return nil }, stub.Expect{
+ {"lowlastcap signaled cancel forward error", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
/* entrypoint */
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
@@ -1987,6 +2006,7 @@ func TestInitEntrypoint(t *testing.T) {
SeccompDisable: true,
ParentPerm: 0750,
}, 1971, 127, 2, false}, uintptr(0x39)}, stub.UniqueError(10), nil),
+ call("swapVerbose", stub.ExpectArgs{false}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("16777216 1971 1\n"), os.FileMode(0)}, nil, nil),
@@ -2039,7 +2059,7 @@ func TestInitEntrypoint(t *testing.T) {
call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
- call("suspend", stub.ExpectArgs{}, nil, nil),
+ call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(10)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- CancelSignal }, []os.Signal{syscall.SIGINT, syscall.SIGTERM}}, nil, nil),
@@ -2065,7 +2085,7 @@ func TestInitEntrypoint(t *testing.T) {
}}},
}, nil},
- {"lowlastcap signaled cancel", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseFalse); return nil }, stub.Expect{
+ {"lowlastcap signaled cancel", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
/* entrypoint */
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
@@ -2086,6 +2106,7 @@ func TestInitEntrypoint(t *testing.T) {
SeccompDisable: true,
ParentPerm: 0750,
}, 1971, 127, 2, false}, uintptr(0x39)}, stub.UniqueError(7), nil),
+ call("swapVerbose", stub.ExpectArgs{false}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("16777216 1971 1\n"), os.FileMode(0)}, nil, nil),
@@ -2138,7 +2159,7 @@ func TestInitEntrypoint(t *testing.T) {
call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
- call("suspend", stub.ExpectArgs{}, nil, nil),
+ call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(7)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{func(c chan<- os.Signal) { c <- os.Interrupt }, []os.Signal{syscall.SIGINT, syscall.SIGTERM}}, nil, nil),
@@ -2155,7 +2176,7 @@ func TestInitEntrypoint(t *testing.T) {
}}},
}, nil},
- {"lowlastcap signaled timeout", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseFalse); return nil }, stub.Expect{
+ {"lowlastcap signaled timeout", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
/* entrypoint */
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
@@ -2176,6 +2197,7 @@ func TestInitEntrypoint(t *testing.T) {
SeccompDisable: true,
ParentPerm: 0750,
}, 1971, 127, 2, false}, uintptr(0x39)}, stub.UniqueError(5), nil),
+ call("swapVerbose", stub.ExpectArgs{false}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("16777216 1971 1\n"), os.FileMode(0)}, nil, nil),
@@ -2228,7 +2250,7 @@ func TestInitEntrypoint(t *testing.T) {
call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
- call("suspend", stub.ExpectArgs{}, nil, nil),
+ call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(5)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{syscall.SIGINT, syscall.SIGTERM}}, nil, nil),
@@ -2247,7 +2269,7 @@ func TestInitEntrypoint(t *testing.T) {
}}},
}, nil},
- {"lowlastcap signaled", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseFalse); return nil }, stub.Expect{
+ {"lowlastcap signaled", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
/* entrypoint */
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
@@ -2268,6 +2290,7 @@ func TestInitEntrypoint(t *testing.T) {
SeccompDisable: true,
ParentPerm: 0750,
}, 1971, 127, 2, false}, uintptr(0x39)}, stub.UniqueError(3), nil),
+ call("swapVerbose", stub.ExpectArgs{false}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("16777216 1971 1\n"), os.FileMode(0)}, nil, nil),
@@ -2320,7 +2343,7 @@ func TestInitEntrypoint(t *testing.T) {
call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
- call("suspend", stub.ExpectArgs{}, nil, nil),
+ call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(3)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{syscall.SIGINT, syscall.SIGTERM}}, nil, nil),
@@ -2346,7 +2369,7 @@ func TestInitEntrypoint(t *testing.T) {
}}},
}, nil},
- {"strangewait nopriv notty noseccomp yamafault", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseFalse); return nil }, stub.Expect{
+ {"strangewait nopriv notty noseccomp yamafault", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
/* entrypoint */
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
@@ -2367,6 +2390,7 @@ func TestInitEntrypoint(t *testing.T) {
SeccompDisable: true,
ParentPerm: 0750,
}, 1971, 127, 2, false}, uintptr(0x39)}, stub.UniqueError(1), nil),
+ call("swapVerbose", stub.ExpectArgs{false}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("16777216 1971 1\n"), os.FileMode(0)}, nil, nil),
@@ -2455,7 +2479,7 @@ func TestInitEntrypoint(t *testing.T) {
call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/run/current-system/sw/bin/bash")}}, nil, nil),
call("start", stub.ExpectArgs{"/run/current-system/sw/bin/bash", []string{"bash", "-c", "false"}, ([]string)(nil), "/.hakurei/nonexistent"}, &os.Process{Pid: 0xbad}, nil),
- call("suspend", stub.ExpectArgs{}, nil, nil),
+ call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(1)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{syscall.SIGINT, syscall.SIGTERM}}, nil, nil),
@@ -2481,7 +2505,7 @@ func TestInitEntrypoint(t *testing.T) {
}}},
}, nil},
- {"success", func(k syscallDispatcher) error { initEntrypoint(k, assertPrefix, assertVerboseTrue); return nil }, stub.Expect{
+ {"success", func(k *kstub) error { initEntrypoint(k, k); return nil }, stub.Expect{
/* entrypoint */
Calls: []stub.Call{
call("lockOSThread", stub.ExpectArgs{}, nil, nil),
@@ -2503,6 +2527,7 @@ func TestInitEntrypoint(t *testing.T) {
RetainSession: true,
Privileged: true,
}, 1000, 100, 3, true}, uintptr(9)}, stub.UniqueError(0), nil),
+ call("swapVerbose", stub.ExpectArgs{true}, false, nil),
call("verbose", stub.ExpectArgs{[]any{"received setup parameters"}}, nil, nil),
call("setDumpable", stub.ExpectArgs{uintptr(1)}, nil, nil),
call("writeFile", stub.ExpectArgs{"/proc/self/uid_map", []byte("4294967296 1000 1\n"), os.FileMode(0)}, nil, nil),
@@ -2594,7 +2619,7 @@ func TestInitEntrypoint(t *testing.T) {
call("umask", stub.ExpectArgs{022}, 0, nil),
call("verbosef", stub.ExpectArgs{"starting initial program %s", []any{MustAbs("/bin/zsh")}}, nil, nil),
call("start", stub.ExpectArgs{"/bin/zsh", []string{"zsh", "-c", "exec vim"}, []string{"DISPLAY=:0"}, "/.hakurei"}, &os.Process{Pid: 0xcafe}, nil),
- call("suspend", stub.ExpectArgs{}, nil, nil),
+ call("suspend", stub.ExpectArgs{}, true, nil),
call("printf", stub.ExpectArgs{"cannot close setup pipe: %v", []any{stub.UniqueError(0)}}, nil, nil),
call("New", stub.ExpectArgs{}, nil, nil),
call("notify", stub.ExpectArgs{nil, []os.Signal{syscall.SIGINT, syscall.SIGTERM}}, nil, nil),
diff --git a/container/initbind.go b/container/initbind.go
index ad8bfaf5..43c8310d 100644
--- a/container/initbind.go
+++ b/container/initbind.go
@@ -59,7 +59,7 @@ func (b *BindMountOp) early(_ *setupState, k syscallDispatcher) error {
}
}
-func (b *BindMountOp) apply(_ *setupState, k syscallDispatcher) error {
+func (b *BindMountOp) apply(state *setupState, k syscallDispatcher) error {
if b.sourceFinal == nil {
if b.Flags&BindOptional == 0 {
// unreachable
@@ -92,11 +92,11 @@ func (b *BindMountOp) apply(_ *setupState, k syscallDispatcher) error {
}
if b.sourceFinal.String() == b.Target.String() {
- k.verbosef("mounting %q flags %#x", target, flags)
+ state.Verbosef("mounting %q flags %#x", target, flags)
} else {
- k.verbosef("mounting %q on %q flags %#x", source, target, flags)
+ state.Verbosef("mounting %q on %q flags %#x", source, target, flags)
}
- return k.bindMount(source, target, flags)
+ return k.bindMount(state, source, target, flags)
}
func (b *BindMountOp) Is(op Op) bool {
diff --git a/container/initdev.go b/container/initdev.go
index cfb4b8b7..335cac06 100644
--- a/container/initdev.go
+++ b/container/initdev.go
@@ -46,6 +46,7 @@ func (d *MountDevOp) apply(state *setupState, k syscallDispatcher) error {
return err
}
if err := k.bindMount(
+ state,
toHost(FHSDev+name),
targetPath,
0,
@@ -93,6 +94,7 @@ func (d *MountDevOp) apply(state *setupState, k syscallDispatcher) error {
if name, err := k.readlink(hostProc.stdout()); err != nil {
return err
} else if err = k.bindMount(
+ state,
toHost(name),
consolePath,
0,
@@ -116,7 +118,7 @@ func (d *MountDevOp) apply(state *setupState, k syscallDispatcher) error {
return nil
}
- if err := k.remount(target, MS_RDONLY); err != nil {
+ if err := k.remount(state, target, MS_RDONLY); err != nil {
return err
}
return k.mountTmpfs(SourceTmpfs, devShmPath, MS_NOSUID|MS_NODEV, 0, 01777)
diff --git a/container/initplace.go b/container/initplace.go
index 5775ad7f..5975bba2 100644
--- a/container/initplace.go
+++ b/container/initplace.go
@@ -52,6 +52,7 @@ func (t *TmpfileOp) apply(state *setupState, k syscallDispatcher) error {
if err := k.ensureFile(target, 0444, state.ParentPerm); err != nil {
return err
} else if err = k.bindMount(
+ state,
tmpPath,
target,
syscall.MS_RDONLY|syscall.MS_NODEV,
diff --git a/container/initremount.go b/container/initremount.go
index 09f141db..b89ed543 100644
--- a/container/initremount.go
+++ b/container/initremount.go
@@ -21,8 +21,8 @@ type RemountOp struct {
func (r *RemountOp) Valid() bool { return r != nil && r.Target != nil }
func (*RemountOp) early(*setupState, syscallDispatcher) error { return nil }
-func (r *RemountOp) apply(_ *setupState, k syscallDispatcher) error {
- return k.remount(toSysroot(r.Target.String()), r.Flags)
+func (r *RemountOp) apply(state *setupState, k syscallDispatcher) error {
+ return k.remount(state, toSysroot(r.Target.String()), r.Flags)
}
func (r *RemountOp) Is(op Op) bool {
diff --git a/container/mount.go b/container/mount.go
index c5a82aaa..dbd39cb9 100644
--- a/container/mount.go
+++ b/container/mount.go
@@ -96,18 +96,17 @@ const (
)
// bindMount mounts source on target and recursively applies flags if MS_REC is set.
-func (p *procPaths) bindMount(source, target string, flags uintptr) error {
+func (p *procPaths) bindMount(msg Msg, source, target string, flags uintptr) error {
// syscallDispatcher.bindMount and procPaths.remount must not be called from this function
if err := p.k.mount(source, target, FstypeNULL, MS_SILENT|MS_BIND|flags&MS_REC, zeroString); err != nil {
return err
}
-
- return p.k.remount(target, flags)
+ return p.k.remount(msg, target, flags)
}
// remount applies flags on target, recursively if MS_REC is set.
-func (p *procPaths) remount(target string, flags uintptr) error {
+func (p *procPaths) remount(msg Msg, target string, flags uintptr) error {
// syscallDispatcher methods bindMount, remount must not be called from this function
var targetFinal string
@@ -116,7 +115,7 @@ func (p *procPaths) remount(target string, flags uintptr) error {
} else {
targetFinal = v
if targetFinal != target {
- p.k.verbosef("target resolves to %q", targetFinal)
+ msg.Verbosef("target resolves to %q", targetFinal)
}
}
@@ -146,7 +145,7 @@ func (p *procPaths) remount(target string, flags uintptr) error {
return err
}
- if err = remountWithFlags(p.k, n, mf); err != nil {
+ if err = remountWithFlags(p.k, msg, n, mf); err != nil {
return err
}
if flags&MS_REC == 0 {
@@ -159,7 +158,7 @@ func (p *procPaths) remount(target string, flags uintptr) error {
continue
}
- if err = remountWithFlags(p.k, cur, mf); err != nil && !errors.Is(err, EACCES) {
+ if err = remountWithFlags(p.k, msg, cur, mf); err != nil && !errors.Is(err, EACCES) {
return err
}
}
@@ -169,12 +168,12 @@ func (p *procPaths) remount(target string, flags uintptr) error {
}
// remountWithFlags remounts mount point described by [vfs.MountInfoNode].
-func remountWithFlags(k syscallDispatcher, n *vfs.MountInfoNode, mf uintptr) error {
+func remountWithFlags(k syscallDispatcher, msg Msg, n *vfs.MountInfoNode, mf uintptr) error {
// syscallDispatcher methods bindMount, remount must not be called from this function
kf, unmatched := n.Flags()
if len(unmatched) != 0 {
- k.verbosef("unmatched vfs options: %q", unmatched)
+ msg.Verbosef("unmatched vfs options: %q", unmatched)
}
if kf&mf != mf {
diff --git a/container/mount_test.go b/container/mount_test.go
index 5d274e26..5391623c 100644
--- a/container/mount_test.go
+++ b/container/mount_test.go
@@ -11,21 +11,21 @@ import (
func TestBindMount(t *testing.T) {
checkSimple(t, "bindMount", []simpleTestCase{
- {"mount", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/nix", syscall.MS_RDONLY)
+ {"mount", func(k *kstub) error {
+ return newProcPaths(k, hostPath).bindMount(nil, "/host/nix", "/sysroot/nix", syscall.MS_RDONLY)
}, stub.Expect{Calls: []stub.Call{
call("mount", stub.ExpectArgs{"/host/nix", "/sysroot/nix", "", uintptr(0x9000), ""}, nil, stub.UniqueError(0xbad)),
}}, stub.UniqueError(0xbad)},
- {"success ne", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/.host-nix", syscall.MS_RDONLY)
+ {"success ne", func(k *kstub) error {
+ return newProcPaths(k, hostPath).bindMount(k, "/host/nix", "/sysroot/.host-nix", syscall.MS_RDONLY)
}, stub.Expect{Calls: []stub.Call{
call("mount", stub.ExpectArgs{"/host/nix", "/sysroot/.host-nix", "", uintptr(0x9000), ""}, nil, nil),
call("remount", stub.ExpectArgs{"/sysroot/.host-nix", uintptr(1)}, nil, nil),
}}, nil},
- {"success", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).bindMount("/host/nix", "/sysroot/nix", syscall.MS_RDONLY)
+ {"success", func(k *kstub) error {
+ return newProcPaths(k, hostPath).bindMount(k, "/host/nix", "/sysroot/nix", syscall.MS_RDONLY)
}, stub.Expect{Calls: []stub.Call{
call("mount", stub.ExpectArgs{"/host/nix", "/sysroot/nix", "", uintptr(0x9000), ""}, nil, nil),
call("remount", stub.ExpectArgs{"/sysroot/nix", uintptr(1)}, nil, nil),
@@ -77,29 +77,29 @@ func TestRemount(t *testing.T) {
416 415 0:30 / /sysroot/nix/store ro,relatime master:5 - overlay overlay rw,lowerdir=/mnt-root/nix/.ro-store,upperdir=/mnt-root/nix/.rw-store/upper,workdir=/mnt-root/nix/.rw-store/work`
checkSimple(t, "remount", []simpleTestCase{
- {"evalSymlinks", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"evalSymlinks", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", stub.UniqueError(6)),
}}, stub.UniqueError(6)},
- {"open", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"open", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, stub.UniqueError(5)),
}}, &os.PathError{Op: "open", Path: "/sysroot/nix", Err: stub.UniqueError(5)}},
- {"readlink", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"readlink", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
call("readlink", stub.ExpectArgs{"/host/proc/self/fd/3735928559"}, "/sysroot/nix", stub.UniqueError(4)),
}}, stub.UniqueError(4)},
- {"close", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"close", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
@@ -107,8 +107,8 @@ func TestRemount(t *testing.T) {
call("close", stub.ExpectArgs{0xdeadbeef}, nil, stub.UniqueError(3)),
}}, &os.PathError{Op: "close", Path: "/sysroot/nix", Err: stub.UniqueError(3)}},
- {"mountinfo no match", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"mountinfo no match", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(k, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/.hakurei", nil),
call("verbosef", stub.ExpectArgs{"target resolves to %q", []any{"/sysroot/.hakurei"}}, nil, nil),
@@ -118,8 +118,8 @@ func TestRemount(t *testing.T) {
call("openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile(sampleMountinfoNix), nil),
}}, &vfs.DecoderError{Op: "unfold", Line: -1, Err: vfs.UnfoldTargetError("/sysroot/.hakurei")}},
- {"mountinfo", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"mountinfo", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
@@ -128,8 +128,8 @@ func TestRemount(t *testing.T) {
call("openNew", stub.ExpectArgs{"/host/proc/self/mountinfo"}, newConstFile("\x00"), nil),
}}, &vfs.DecoderError{Op: "parse", Line: 0, Err: vfs.ErrMountInfoFields}},
- {"mount", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"mount", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
@@ -139,8 +139,8 @@ func TestRemount(t *testing.T) {
call("mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, stub.UniqueError(2)),
}}, stub.UniqueError(2)},
- {"mount propagate", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"mount propagate", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
@@ -151,8 +151,8 @@ func TestRemount(t *testing.T) {
call("mount", stub.ExpectArgs{"none", "/sysroot/nix/.ro-store", "", uintptr(0x209027), ""}, nil, stub.UniqueError(1)),
}}, stub.UniqueError(1)},
- {"success toplevel", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/bin", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"success toplevel", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/bin", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/bin"}, "/sysroot/bin", nil),
call("open", stub.ExpectArgs{"/sysroot/bin", 0x280000, uint32(0)}, 0xbabe, nil),
@@ -162,8 +162,8 @@ func TestRemount(t *testing.T) {
call("mount", stub.ExpectArgs{"none", "/sysroot/bin", "", uintptr(0x209027), ""}, nil, nil),
}}, nil},
- {"success EACCES", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"success EACCES", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
@@ -175,8 +175,8 @@ func TestRemount(t *testing.T) {
call("mount", stub.ExpectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil),
}}, nil},
- {"success no propagate", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"success no propagate", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
@@ -186,8 +186,8 @@ func TestRemount(t *testing.T) {
call("mount", stub.ExpectArgs{"none", "/sysroot/nix", "", uintptr(0x209027), ""}, nil, nil),
}}, nil},
- {"success case sensitive", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"success case sensitive", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(nil, "/sysroot/nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/nix"}, "/sysroot/nix", nil),
call("open", stub.ExpectArgs{"/sysroot/nix", 0x280000, uint32(0)}, 0xdeadbeef, nil),
@@ -199,8 +199,8 @@ func TestRemount(t *testing.T) {
call("mount", stub.ExpectArgs{"none", "/sysroot/nix/store", "", uintptr(0x209027), ""}, nil, nil),
}}, nil},
- {"success", func(k syscallDispatcher) error {
- return newProcPaths(k, hostPath).remount("/sysroot/.nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
+ {"success", func(k *kstub) error {
+ return newProcPaths(k, hostPath).remount(k, "/sysroot/.nix", syscall.MS_REC|syscall.MS_RDONLY|syscall.MS_NODEV)
}, stub.Expect{Calls: []stub.Call{
call("evalSymlinks", stub.ExpectArgs{"/sysroot/.nix"}, "/sysroot/NIX", nil),
call("verbosef", stub.ExpectArgs{"target resolves to %q", []any{"/sysroot/NIX"}}, nil, nil),
@@ -217,18 +217,18 @@ func TestRemount(t *testing.T) {
func TestRemountWithFlags(t *testing.T) {
checkSimple(t, "remountWithFlags", []simpleTestCase{
- {"noop unmatched", func(k syscallDispatcher) error {
- return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime,cat"}}, 0)
+ {"noop unmatched", func(k *kstub) error {
+ return remountWithFlags(k, k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime,cat"}}, 0)
}, stub.Expect{Calls: []stub.Call{
call("verbosef", stub.ExpectArgs{"unmatched vfs options: %q", []any{[]string{"cat"}}}, nil, nil),
}}, nil},
- {"noop", func(k syscallDispatcher) error {
- return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, 0)
+ {"noop", func(k *kstub) error {
+ return remountWithFlags(k, nil, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, 0)
}, stub.Expect{}, nil},
- {"success", func(k syscallDispatcher) error {
- return remountWithFlags(k, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, syscall.MS_RDONLY)
+ {"success", func(k *kstub) error {
+ return remountWithFlags(k, nil, &vfs.MountInfoNode{MountInfoEntry: &vfs.MountInfoEntry{VfsOptstr: "rw,relatime"}}, syscall.MS_RDONLY)
}, stub.Expect{Calls: []stub.Call{
call("mount", stub.ExpectArgs{"none", "", "", uintptr(0x209021), ""}, nil, nil),
}}, nil},
@@ -237,20 +237,20 @@ func TestRemountWithFlags(t *testing.T) {
func TestMountTmpfs(t *testing.T) {
checkSimple(t, "mountTmpfs", []simpleTestCase{
- {"mkdirAll", func(k syscallDispatcher) error {
+ {"mkdirAll", func(k *kstub) error {
return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 1<<10, 0700)
}, stub.Expect{Calls: []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/run/user/1000", os.FileMode(0700)}, nil, stub.UniqueError(0)),
}}, stub.UniqueError(0)},
- {"success no size", func(k syscallDispatcher) error {
+ {"success no size", func(k *kstub) error {
return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 0, 0710)
}, stub.Expect{Calls: []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/run/user/1000", os.FileMode(0750)}, nil, nil),
call("mount", stub.ExpectArgs{"ephemeral", "/sysroot/run/user/1000", "tmpfs", uintptr(0), "mode=0710"}, nil, nil),
}}, nil},
- {"success", func(k syscallDispatcher) error {
+ {"success", func(k *kstub) error {
return mountTmpfs(k, "ephemeral", "/sysroot/run/user/1000", 0, 1<<10, 0700)
}, stub.Expect{Calls: []stub.Call{
call("mkdirAll", stub.ExpectArgs{"/sysroot/run/user/1000", os.FileMode(0700)}, nil, nil),
diff --git a/container/msg.go b/container/msg.go
index 1512a583..38c38ecd 100644
--- a/container/msg.go
+++ b/container/msg.go
@@ -23,45 +23,88 @@ func GetErrorMessage(err error) (string, bool) {
return e.Message(), true
}
+// Msg is used for package-wide verbose logging.
type Msg interface {
+ // GetLogger returns the address of the underlying [log.Logger].
+ GetLogger() *log.Logger
+
+ // IsVerbose atomically loads and returns whether [Msg] has verbose logging enabled.
IsVerbose() bool
+ // SwapVerbose atomically stores a new verbose state and returns the previous value held by [Msg].
+ SwapVerbose(verbose bool) bool
+ // Verbose passes its argument to the Println method of the underlying [log.Logger] if IsVerbose returns true.
Verbose(v ...any)
+ // Verbosef passes its argument to the Printf method of the underlying [log.Logger] if IsVerbose returns true.
Verbosef(format string, v ...any)
- Suspend()
+ // Suspend causes the embedded [Suspendable] to withhold writes to its downstream [io.Writer].
+ // Suspend returns false and is a noop if called between calls to Suspend and Resume.
+ Suspend() bool
+
+ // Resume dumps the entire buffer held by the embedded [Suspendable] and stops withholding future writes.
+ // Resume returns false and is a noop if a call to Suspend does not precede it.
Resume() bool
+
+ // BeforeExit runs implementation-specific cleanup code, and optionally prints warnings.
+ // BeforeExit is called before [os.Exit].
BeforeExit()
}
-type DefaultMsg struct{ inactive atomic.Bool }
+// defaultMsg is the default implementation of the [Msg] interface.
+// The zero value is not safe for use. Callers should use the [NewMsg] function instead.
+type defaultMsg struct {
+ verbose atomic.Bool
-func (msg *DefaultMsg) IsVerbose() bool { return true }
-func (msg *DefaultMsg) Verbose(v ...any) {
- if !msg.inactive.Load() {
- log.Println(v...)
- }
+ logger *log.Logger
+ Suspendable
}
-func (msg *DefaultMsg) Verbosef(format string, v ...any) {
- if !msg.inactive.Load() {
- log.Printf(format, v...)
+
+// NewMsg initialises a downstream [log.Logger] for a new [Msg].
+// The [log.Logger] should no longer be configured after NewMsg returns.
+// If downstream is nil, a new logger is initialised in its place.
+func NewMsg(downstream *log.Logger) Msg {
+ if downstream == nil {
+ downstream = log.New(log.Writer(), "container: ", 0)
}
+
+ m := defaultMsg{logger: downstream}
+ m.Suspendable.Downstream = downstream.Writer()
+ downstream.SetOutput(&m.Suspendable)
+ return &m
}
-func (msg *DefaultMsg) Suspend() { msg.inactive.Store(true) }
-func (msg *DefaultMsg) Resume() bool { return msg.inactive.CompareAndSwap(true, false) }
-func (msg *DefaultMsg) BeforeExit() {}
+func (msg *defaultMsg) GetLogger() *log.Logger { return msg.logger }
-// msg is the [Msg] implemented used by all exported [container] functions.
-var msg Msg = new(DefaultMsg)
+func (msg *defaultMsg) IsVerbose() bool { return msg.verbose.Load() }
+func (msg *defaultMsg) SwapVerbose(verbose bool) bool { return msg.verbose.Swap(verbose) }
+func (msg *defaultMsg) Verbose(v ...any) {
+ if msg.verbose.Load() {
+ msg.logger.Println(v...)
+ }
+}
+func (msg *defaultMsg) Verbosef(format string, v ...any) {
+ if msg.verbose.Load() {
+ msg.logger.Printf(format, v...)
+ }
+}
-// GetOutput returns the current active [Msg] implementation.
-func GetOutput() Msg { return msg }
+// Resume calls [Suspendable.Resume] and prints a message if buffer was filled
+// between calls to [Suspendable.Suspend] and Resume.
+func (msg *defaultMsg) Resume() bool {
+ resumed, dropped, _, err := msg.Suspendable.Resume()
+ if err != nil {
+ // probably going to result in an error as well, so this message is as good as unreachable
+ msg.logger.Printf("cannot dump buffer on resume: %v", err)
+ }
+ if resumed && dropped > 0 {
+ msg.logger.Printf("dropped %d bytes while output is suspended", dropped)
+ }
+ return resumed
+}
-// SetOutput replaces the current active [Msg] implementation.
-func SetOutput(v Msg) {
- if v == nil {
- msg = new(DefaultMsg)
- } else {
- msg = v
+// BeforeExit prints a message if called between calls to [Suspendable.Suspend] and Resume.
+func (msg *defaultMsg) BeforeExit() {
+ if msg.Resume() {
+ msg.logger.Printf("beforeExit reached on suspended output")
}
}
diff --git a/container/msg_test.go b/container/msg_test.go
index 1b36a1dd..ed5c0c59 100644
--- a/container/msg_test.go
+++ b/container/msg_test.go
@@ -1,14 +1,16 @@
package container_test
import (
+ "bytes"
"errors"
+ "io"
"log"
"strings"
- "sync/atomic"
"syscall"
"testing"
"hakurei.app/container"
+ "hakurei.app/container/stub"
)
func TestMessageError(t *testing.T) {
@@ -39,146 +41,137 @@ func TestMessageError(t *testing.T) {
}
func TestDefaultMsg(t *testing.T) {
- {
- w := log.Writer()
- f := log.Flags()
- t.Cleanup(func() { log.SetOutput(w); log.SetFlags(f) })
- }
- msg := new(container.DefaultMsg)
+ // copied from output.go
+ const suspendBufMax = 1 << 24
- t.Run("is verbose", func(t *testing.T) {
- if !msg.IsVerbose() {
- t.Error("IsVerbose unexpected outcome")
- }
- })
+ t.Run("logger", func(t *testing.T) {
+ t.Run("nil", func(t *testing.T) {
+ got := container.NewMsg(nil).GetLogger()
- t.Run("verbose", func(t *testing.T) {
- log.SetOutput(panicWriter{})
- msg.Suspend()
- msg.Verbose()
- msg.Verbosef("\x00")
- msg.Resume()
+ if out := got.Writer().(*container.Suspendable).Downstream; out != log.Writer() {
+ t.Errorf("GetLogger: Downstream = %#v", out)
+ }
- buf := new(strings.Builder)
- log.SetOutput(buf)
- log.SetFlags(0)
- msg.Verbose()
- msg.Verbosef("\x00")
+ if prefix := got.Prefix(); prefix != "container: " {
+ t.Errorf("GetLogger: prefix = %q", prefix)
+ }
+ })
- want := "\n\x00\n"
- if buf.String() != want {
- t.Errorf("Verbose: %q, want %q", buf.String(), want)
- }
- })
+ t.Run("takeover", func(t *testing.T) {
+ l := log.New(io.Discard, "\x00", 0xdeadbeef)
+ got := container.NewMsg(l)
- t.Run("inactive", func(t *testing.T) {
- {
- inactive := msg.Resume()
- if inactive {
- t.Cleanup(func() { msg.Suspend() })
+ if logger := got.GetLogger(); logger != l {
+ t.Errorf("GetLogger: %#v, want %#v", logger, l)
}
- }
-
- if msg.Resume() {
- t.Error("Resume unexpected outcome")
- }
- msg.Suspend()
- if !msg.Resume() {
- t.Error("Resume unexpected outcome")
- }
+ if ds := l.Writer().(*container.Suspendable).Downstream; ds != io.Discard {
+ t.Errorf("GetLogger: Downstream = %#v", ds)
+ }
+ })
})
- // the function is a noop
- t.Run("beforeExit", func(t *testing.T) { msg.BeforeExit() })
-}
-
-type panicWriter struct{}
-
-func (panicWriter) Write([]byte) (int, error) { panic("unreachable") }
-
-func saveRestoreOutput(t *testing.T) {
- out := container.GetOutput()
- t.Cleanup(func() { container.SetOutput(out) })
-}
-
-func replaceOutput(t *testing.T) {
- saveRestoreOutput(t)
- container.SetOutput(&testOutput{t: t})
-}
+ dw := expectWriter{t: t}
-type testOutput struct {
- t *testing.T
- suspended atomic.Bool
-}
+ steps := []struct {
+ name string
+ pt, next []byte
+ err error
-func (out *testOutput) IsVerbose() bool { return testing.Verbose() }
+ f func(t *testing.T, msg container.Msg)
+ }{
+ {"zero verbose", nil, nil, nil, func(t *testing.T, msg container.Msg) {
+ if msg.IsVerbose() {
+ t.Error("IsVerbose unexpected true")
+ }
+ }},
-func (out *testOutput) Verbose(v ...any) {
- if !out.IsVerbose() {
- return
- }
- out.t.Log(v...)
-}
+ {"swap false", nil, nil, nil, func(t *testing.T, msg container.Msg) {
+ if msg.SwapVerbose(false) {
+ t.Error("SwapVerbose unexpected true")
+ }
+ }},
+ {"write discard", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.Verbose("\x00")
+ msg.Verbosef("\x00")
+ }},
+ {"verbose false", nil, nil, nil, func(t *testing.T, msg container.Msg) {
+ if msg.IsVerbose() {
+ t.Error("IsVerbose unexpected true")
+ }
+ }},
-func (out *testOutput) Verbosef(format string, v ...any) {
- if !out.IsVerbose() {
- return
- }
- out.t.Logf(format, v...)
-}
+ {"swap true", nil, nil, nil, func(t *testing.T, msg container.Msg) {
+ if msg.SwapVerbose(true) {
+ t.Error("SwapVerbose unexpected true")
+ }
+ }},
+ {"write verbose", []byte("test: \x00\n"), nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.Verbose("\x00")
+ }},
+ {"write verbosef", []byte(`test: "\x00"` + "\n"), nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.Verbosef("%q", "\x00")
+ }},
+ {"verbose true", nil, nil, nil, func(t *testing.T, msg container.Msg) {
+ if !msg.IsVerbose() {
+ t.Error("IsVerbose unexpected false")
+ }
+ }},
-func (out *testOutput) Suspend() {
- if out.suspended.CompareAndSwap(false, true) {
- out.Verbose("suspend called")
- return
- }
- out.Verbose("suspend called on suspended output")
-}
+ {"resume noop", nil, nil, nil, func(t *testing.T, msg container.Msg) {
+ if msg.Resume() {
+ t.Error("Resume unexpected success")
+ }
+ }},
+ {"beforeExit noop", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.BeforeExit()
+ }},
-func (out *testOutput) Resume() bool {
- if out.suspended.CompareAndSwap(true, false) {
- out.Verbose("resume called")
- return true
- }
- out.Verbose("resume called on unsuspended output")
- return false
-}
+ {"beforeExit suspend", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.Suspend()
+ }},
+ {"beforeExit message", []byte("test: beforeExit reached on suspended output\n"), nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.BeforeExit()
+ }},
+ {"post beforeExit resume noop", nil, nil, nil, func(t *testing.T, msg container.Msg) {
+ if msg.Resume() {
+ t.Error("Resume unexpected success")
+ }
+ }},
-func (out *testOutput) BeforeExit() { out.Verbose("beforeExit called") }
+ {"suspend", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.Suspend()
+ }},
+ {"suspend write", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.GetLogger().Print("\x00")
+ }},
+ {"resume error", []byte("test: \x00\n"), []byte("test: cannot dump buffer on resume: unique error 0 injected by the test suite\n"), stub.UniqueError(0), func(t *testing.T, msg container.Msg) {
+ if !msg.Resume() {
+ t.Error("Resume unexpected failure")
+ }
+ }},
-func TestGetSetOutput(t *testing.T) {
- {
- out := container.GetOutput()
- t.Cleanup(func() { container.SetOutput(out) })
+ {"suspend drop", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.Suspend()
+ }},
+ {"suspend write fill", nil, nil, nil, func(_ *testing.T, msg container.Msg) {
+ msg.GetLogger().Print(strings.Repeat("\x00", suspendBufMax))
+ }},
+ {"resume dropped", append([]byte("test: "), bytes.Repeat([]byte{0}, suspendBufMax-6)...), []byte("test: dropped 7 bytes while output is suspended\n"), nil, func(t *testing.T, msg container.Msg) {
+ if !msg.Resume() {
+ t.Error("Resume unexpected failure")
+ }
+ }},
}
- t.Run("default", func(t *testing.T) {
- container.SetOutput(new(stubOutput))
- if v, ok := container.GetOutput().(*container.DefaultMsg); ok {
- t.Fatalf("SetOutput: got unexpected output %#v", v)
- }
- container.SetOutput(nil)
- if _, ok := container.GetOutput().(*container.DefaultMsg); !ok {
- t.Fatalf("SetOutput: got unexpected output %#v", container.GetOutput())
- }
- })
-
- t.Run("stub", func(t *testing.T) {
- container.SetOutput(new(stubOutput))
- if _, ok := container.GetOutput().(*stubOutput); !ok {
- t.Fatalf("SetOutput: got unexpected output %#v", container.GetOutput())
+ msg := container.NewMsg(log.New(&dw, "test: ", 0))
+ for _, step := range steps {
+ // these share the same writer, so cannot be subtests
+ t.Logf("running step %q", step.name)
+ dw.expect, dw.next, dw.err = step.pt, step.next, step.err
+ step.f(t, msg)
+ if dw.expect != nil {
+ t.Errorf("expect: %q", string(dw.expect))
}
- })
-}
-
-type stubOutput struct {
- wrapF func(error, ...any) error
+ }
}
-
-func (*stubOutput) IsVerbose() bool { panic("unreachable") }
-func (*stubOutput) Verbose(...any) { panic("unreachable") }
-func (*stubOutput) Verbosef(string, ...any) { panic("unreachable") }
-func (*stubOutput) Suspend() { panic("unreachable") }
-func (*stubOutput) Resume() bool { panic("unreachable") }
-func (*stubOutput) BeforeExit() { panic("unreachable") }
diff --git a/container/output_test.go b/container/output_test.go
index cfa8e0de..b6dedf16 100644
--- a/container/output_test.go
+++ b/container/output_test.go
@@ -28,7 +28,7 @@ func TestSuspendable(t *testing.T) {
)
// shares the same writer
- testCases := []struct {
+ steps := []struct {
name string
w, pt []byte
err error
@@ -75,25 +75,25 @@ func TestSuspendable(t *testing.T) {
var dw expectWriter
w := container.Suspendable{Downstream: &dw}
- for _, tc := range testCases {
+ for _, step := range steps {
// these share the same writer, so cannot be subtests
- t.Logf("writing step %q", tc.name)
- dw.expect, dw.err = tc.pt, tc.err
+ t.Logf("writing step %q", step.name)
+ dw.expect, dw.err = step.pt, step.err
var (
gotN int
gotErr error
)
- wantN := tc.n
+ wantN := step.n
switch wantN {
case nSpecialPtEquiv:
- wantN = len(tc.pt)
- gotN, gotErr = w.Write(tc.w)
+ wantN = len(step.pt)
+ gotN, gotErr = w.Write(step.w)
case nSpecialWEquiv:
- wantN = len(tc.w)
- gotN, gotErr = w.Write(tc.w)
+ wantN = len(step.w)
+ gotN, gotErr = w.Write(step.w)
case nSpecialSuspend:
s := w.IsSuspended()
@@ -101,8 +101,8 @@ func TestSuspendable(t *testing.T) {
t.Fatal("Suspend: unexpected success")
}
- wantN = len(tc.w)
- gotN, gotErr = w.Write(tc.w)
+ wantN = len(step.w)
+ gotN, gotErr = w.Write(step.w)
default:
if wantN <= nSpecialDump {
@@ -118,10 +118,10 @@ func TestSuspendable(t *testing.T) {
t.Errorf("Resume: dropped = %d, want %d", dropped, wantDropped)
}
- wantN = len(tc.pt)
+ wantN = len(step.pt)
gotN, gotErr = int(n), err
} else {
- gotN, gotErr = w.Write(tc.w)
+ gotN, gotErr = w.Write(step.w)
}
}
@@ -129,9 +129,13 @@ func TestSuspendable(t *testing.T) {
t.Errorf("Write: n = %d, want %d", gotN, wantN)
}
- if !reflect.DeepEqual(gotErr, tc.wantErr) {
+ if !reflect.DeepEqual(gotErr, step.wantErr) {
t.Errorf("Write: %v", gotErr)
}
+
+ if dw.expect != nil {
+ t.Errorf("expect: %q", string(dw.expect))
+ }
}
}
@@ -139,17 +143,31 @@ func TestSuspendable(t *testing.T) {
type expectWriter struct {
expect []byte
err error
+
+ // optional consecutive write
+ next []byte
+
+ // optional, calls Error on failure if not nil
+ t *testing.T
}
func (w *expectWriter) Write(p []byte) (n int, err error) {
- defer func() { w.expect = nil }()
+ defer func() { w.expect = w.next; w.next = nil }()
n, err = len(p), w.err
if w.expect == nil {
- return 0, errors.New("unexpected call to Write: " + strconv.Quote(string(p)))
+ n, err = 0, errors.New("unexpected call to Write: "+strconv.Quote(string(p)))
+ if w.t != nil {
+ w.t.Error(err.Error())
+ }
+ return
}
if string(p) != string(w.expect) {
- return 0, errors.New("p = " + strconv.Quote(string(p)) + ", want " + strconv.Quote(string(w.expect)))
+ n, err = 0, errors.New("p = "+strconv.Quote(string(p))+", want "+strconv.Quote(string(w.expect)))
+ if w.t != nil {
+ w.t.Error(err.Error())
+ }
+ return
}
return
}
diff --git a/container/sysctl.go b/container/sysctl.go
index bfcf2788..327872b5 100644
--- a/container/sysctl.go
+++ b/container/sysctl.go
@@ -2,7 +2,6 @@ package container
import (
"bytes"
- "log"
"os"
"strconv"
"sync"
@@ -22,26 +21,28 @@ const (
kernelCapLastCapPath = FHSProcSys + "kernel/cap_last_cap"
)
-func mustReadSysctl() {
- if v, err := os.ReadFile(kernelOverflowuidPath); err != nil {
- log.Fatalf("cannot read %q: %v", kernelOverflowuidPath, err)
- } else if kernelOverflowuid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
- log.Fatalf("cannot interpret %q: %v", kernelOverflowuidPath, err)
- }
+func mustReadSysctl(msg Msg) {
+ sysctlOnce.Do(func() {
+ if v, err := os.ReadFile(kernelOverflowuidPath); err != nil {
+ msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowuidPath, err)
+ } else if kernelOverflowuid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
+ msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowuidPath, err)
+ }
- if v, err := os.ReadFile(kernelOverflowgidPath); err != nil {
- log.Fatalf("cannot read %q: %v", kernelOverflowgidPath, err)
- } else if kernelOverflowgid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
- log.Fatalf("cannot interpret %q: %v", kernelOverflowgidPath, err)
- }
+ if v, err := os.ReadFile(kernelOverflowgidPath); err != nil {
+ msg.GetLogger().Fatalf("cannot read %q: %v", kernelOverflowgidPath, err)
+ } else if kernelOverflowgid, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
+ msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelOverflowgidPath, err)
+ }
- if v, err := os.ReadFile(kernelCapLastCapPath); err != nil {
- log.Fatalf("cannot read %q: %v", kernelCapLastCapPath, err)
- } else if kernelCapLastCap, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
- log.Fatalf("cannot interpret %q: %v", kernelCapLastCapPath, err)
- }
+ if v, err := os.ReadFile(kernelCapLastCapPath); err != nil {
+ msg.GetLogger().Fatalf("cannot read %q: %v", kernelCapLastCapPath, err)
+ } else if kernelCapLastCap, err = strconv.Atoi(string(bytes.TrimSpace(v))); err != nil {
+ msg.GetLogger().Fatalf("cannot interpret %q: %v", kernelCapLastCapPath, err)
+ }
+ })
}
-func OverflowUid() int { sysctlOnce.Do(mustReadSysctl); return kernelOverflowuid }
-func OverflowGid() int { sysctlOnce.Do(mustReadSysctl); return kernelOverflowgid }
-func LastCap() uintptr { sysctlOnce.Do(mustReadSysctl); return uintptr(kernelCapLastCap) }
+func OverflowUid(msg Msg) int { mustReadSysctl(msg); return kernelOverflowuid }
+func OverflowGid(msg Msg) int { mustReadSysctl(msg); return kernelOverflowgid }
+func LastCap(msg Msg) uintptr { mustReadSysctl(msg); return uintptr(kernelCapLastCap) }