aboutsummaryrefslogtreecommitdiffhomepage
path: root/container/dispatcher.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2025-08-21 21:59:07 +0900
committerOphestra <cat@gensokyo.uk>2025-08-22 19:27:31 +0900
commit09d284498109b847da0da1e2c5f883268a7f2d46 (patch)
tree3784dee4b4e4ec97a95cb222c70d05463ae640ef /container/dispatcher.go
parentd500d6e55917e12a3f98b39cf0d29b6c96de9667 (diff)
container/init: wrap syscall helper functions
This allows tests to stub all kernel behaviour, enabling measurement of all function call arguments and error injection. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'container/dispatcher.go')
-rw-r--r--container/dispatcher.go229
1 files changed, 229 insertions, 0 deletions
diff --git a/container/dispatcher.go b/container/dispatcher.go
new file mode 100644
index 00000000..38ed8f8e
--- /dev/null
+++ b/container/dispatcher.go
@@ -0,0 +1,229 @@
+package container
+
+import (
+ "io"
+ "io/fs"
+ "log"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "syscall"
+
+ "hakurei.app/container/seccomp"
+)
+
+type osFile interface {
+ Name() string
+ io.Writer
+ fs.File
+}
+
+// syscallDispatcher provides methods that make state-dependent system calls as part of their behaviour.
+type syscallDispatcher interface {
+ // setPtracer provides [SetPtracer].
+ setPtracer(pid uintptr) error
+ // setDumpable provides [SetDumpable].
+ setDumpable(dumpable uintptr) error
+ // setNoNewPrivs provides [SetNoNewPrivs].
+ setNoNewPrivs() error
+
+ // lastcap provides [LastCap].
+ lastcap() uintptr
+ // capset provides capset.
+ capset(hdrp *capHeader, datap *[2]capData) error
+ // capBoundingSetDrop provides capBoundingSetDrop.
+ capBoundingSetDrop(cap uintptr) error
+ // capAmbientClearAll provides capAmbientClearAll.
+ capAmbientClearAll() error
+ // capAmbientRaise provides capAmbientRaise.
+ capAmbientRaise(cap uintptr) error
+ // isatty provides [Isatty].
+ isatty(fd int) bool
+ // receive provides [Receive].
+ receive(key string, e any, v **os.File) (closeFunc func() error, err error)
+
+ // bindMount provides procPaths.bindMount.
+ bindMount(source, target string, flags uintptr, eq bool) error
+ // remount provides procPaths.remount.
+ remount(target string, flags uintptr) error
+ // mountTmpfs provides mountTmpfs.
+ mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error
+ // ensureFile provides ensureFile.
+ ensureFile(name string, perm, pperm os.FileMode) error
+
+ // seccompLoad provides [seccomp.Load].
+ seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error
+ // notify provides [signal.Notify].
+ notify(c chan<- os.Signal, sig ...os.Signal)
+ // start starts [os/exec.Cmd].
+ start(c *exec.Cmd) error
+ // signal signals the underlying process of [os/exec.Cmd].
+ signal(c *exec.Cmd, sig os.Signal) error
+ // evalSymlinks provides [filepath.EvalSymlinks].
+ evalSymlinks(path string) (string, error)
+
+ // exit provides [os.Exit].
+ exit(code int)
+ // getpid provides [os.Getpid].
+ getpid() int
+ // stat provides [os.Stat].
+ stat(name string) (os.FileInfo, error)
+ // mkdir provides [os.Mkdir].
+ mkdir(name string, perm os.FileMode) error
+ // mkdirTemp provides [os.MkdirTemp].
+ mkdirTemp(dir, pattern string) (string, error)
+ // mkdirAll provides [os.MkdirAll].
+ mkdirAll(path string, perm os.FileMode) error
+ // readdir provides [os.ReadDir].
+ readdir(name string) ([]os.DirEntry, error)
+ // writeFile provides [os.WriteFile].
+ writeFile(name string, data []byte, perm os.FileMode) error
+ // createTemp provides [os.CreateTemp].
+ createTemp(dir, pattern string) (osFile, error)
+ // remove provides os.Remove.
+ remove(name string) error
+ // newFile provides os.NewFile.
+ newFile(fd uintptr, name string) *os.File
+ // symlink provides os.Symlink.
+ symlink(oldname, newname string) error
+ // readlink provides [os.Readlink].
+ readlink(name string) (string, error)
+
+ // umask provides syscall.Umask.
+ umask(mask int) (oldmask int)
+ // sethostname provides syscall.Sethostname
+ sethostname(p []byte) (err error)
+ // chdir provides syscall.Chdir
+ chdir(path string) (err error)
+ // fchdir provides syscall.Fchdir
+ fchdir(fd int) (err error)
+ // open provides syscall.Open
+ open(path string, mode int, perm uint32) (fd int, err error)
+ // close provides syscall.Close
+ close(fd int) (err error)
+ // pivotRoot provides syscall.PivotRoot
+ pivotRoot(newroot, putold string) (err error)
+ // mount provides syscall.Mount
+ mount(source, target, fstype string, flags uintptr, data string) (err error)
+ // unmount provides syscall.Unmount
+ unmount(target string, flags int) (err error)
+ // 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()
+ // printBaseErr provides [Msg.PrintBaseErr].
+ printBaseErr(err error, fallback string)
+}
+
+// direct implements syscallDispatcher on the current kernel.
+type direct struct{}
+
+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) 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() }
+func (direct) capAmbientRaise(cap uintptr) error { return capAmbientRaise(cap) }
+func (direct) isatty(fd int) bool { return Isatty(fd) }
+func (direct) receive(key string, e any, v **os.File) (func() error, error) {
+ return Receive(key, e, v)
+}
+
+func (direct) bindMount(source, target string, flags uintptr, eq bool) error {
+ return hostProc.bindMount(source, target, flags, eq)
+}
+func (direct) remount(target string, flags uintptr) error {
+ return hostProc.remount(target, flags)
+}
+func (direct) mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error {
+ return mountTmpfs(fsname, target, flags, size, perm)
+}
+func (direct) ensureFile(name string, perm, pperm os.FileMode) error {
+ return ensureFile(name, perm, pperm)
+}
+
+func (direct) seccompLoad(rules []seccomp.NativeRule, flags seccomp.ExportFlag) error {
+ return seccomp.Load(rules, flags)
+}
+func (direct) notify(c chan<- os.Signal, sig ...os.Signal) { signal.Notify(c, sig...) }
+func (direct) start(c *exec.Cmd) error { return c.Start() }
+func (direct) signal(c *exec.Cmd, sig os.Signal) error { return c.Process.Signal(sig) }
+func (direct) evalSymlinks(path string) (string, error) { return filepath.EvalSymlinks(path) }
+
+func (direct) exit(code int) { os.Exit(code) }
+func (direct) getpid() int { return os.Getpid() }
+func (direct) stat(name string) (os.FileInfo, error) { return os.Stat(name) }
+func (direct) mkdir(name string, perm os.FileMode) error { return os.Mkdir(name, perm) }
+func (direct) mkdirTemp(dir, pattern string) (string, error) { return os.MkdirTemp(dir, pattern) }
+func (direct) mkdirAll(path string, perm os.FileMode) error { return os.MkdirAll(path, perm) }
+func (direct) readdir(name string) ([]os.DirEntry, error) { return os.ReadDir(name) }
+func (direct) writeFile(name string, data []byte, perm os.FileMode) error {
+ return os.WriteFile(name, data, perm)
+}
+func (direct) createTemp(dir, pattern string) (osFile, error) {
+ return os.CreateTemp(dir, pattern)
+}
+func (direct) remove(name string) error {
+ return os.Remove(name)
+}
+func (direct) newFile(fd uintptr, name string) *os.File {
+ return os.NewFile(fd, name)
+}
+func (direct) symlink(oldname, newname string) error {
+ return os.Symlink(oldname, newname)
+}
+func (direct) readlink(name string) (string, error) {
+ return os.Readlink(name)
+}
+
+func (direct) umask(mask int) (oldmask int) { return syscall.Umask(mask) }
+func (direct) sethostname(p []byte) (err error) { return syscall.Sethostname(p) }
+func (direct) chdir(path string) (err error) { return syscall.Chdir(path) }
+func (direct) fchdir(fd int) (err error) { return syscall.Fchdir(fd) }
+func (direct) open(path string, mode int, perm uint32) (fd int, err error) {
+ return syscall.Open(path, mode, perm)
+}
+func (direct) close(fd int) (err error) {
+ return syscall.Close(fd)
+}
+func (direct) pivotRoot(newroot, putold string) (err error) {
+ return syscall.PivotRoot(newroot, putold)
+}
+func (direct) mount(source, target, fstype string, flags uintptr, data string) (err error) {
+ return syscall.Mount(source, target, fstype, flags, data)
+}
+func (direct) unmount(target string, flags int) (err error) {
+ return syscall.Unmount(target, flags)
+}
+func (direct) wait4(pid int, wstatus *syscall.WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) {
+ 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) printBaseErr(err error, fallback string) { msg.PrintBaseErr(err, fallback) }