aboutsummaryrefslogtreecommitdiffhomepage
path: root/helper/bwrap.go
diff options
context:
space:
mode:
authorOphestra Umiker <cat@ophivana.moe>2024-10-08 18:02:38 +0900
committerOphestra Umiker <cat@ophivana.moe>2024-10-08 18:02:38 +0900
commit7c7999e9e527bc245560ea6804a867276acaae6f (patch)
tree903be2bec90b79b9202f91ec4cede8c869abd4fb /helper/bwrap.go
parentc6223771dbc9cf8a4a44424759591adb496f2ce5 (diff)
helper: implementation of helper.Helper using bwrap
Signed-off-by: Ophestra Umiker <cat@ophivana.moe>
Diffstat (limited to 'helper/bwrap.go')
-rw-r--r--helper/bwrap.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/helper/bwrap.go b/helper/bwrap.go
new file mode 100644
index 00000000..4ac1032c
--- /dev/null
+++ b/helper/bwrap.go
@@ -0,0 +1,144 @@
+package helper
+
+import (
+ "errors"
+ "io"
+ "os/exec"
+ "strconv"
+ "sync"
+
+ "git.ophivana.moe/cat/fortify/helper/bwrap"
+)
+
+// BubblewrapName is the file name or path to bubblewrap.
+var BubblewrapName = "bwrap"
+
+type bubblewrap struct {
+ // bwrap child file name
+ name string
+
+ // bwrap pipes
+ p *pipes
+ // sealed bwrap config
+ config *bwrap.Config
+ // returns an array of arguments passed directly
+ // to the child process spawned by bwrap
+ argF func(argsFD, statFD int) []string
+
+ // pipes received by the child
+ // nil if no pipes are required
+ cp *pipes
+
+ lock sync.RWMutex
+ *exec.Cmd
+}
+
+func (b *bubblewrap) StartNotify(ready chan error) error {
+ b.lock.Lock()
+ defer b.lock.Unlock()
+
+ if ready != nil && b.cp == nil {
+ panic("attempted to start with status monitoring on a bwrap child initialised without pipes")
+ }
+
+ // Check for doubled Start calls before we defer failure cleanup. If the prior
+ // call to Start succeeded, we don't want to spuriously close its pipes.
+ if b.Cmd.Process != nil {
+ return errors.New("exec: already started")
+ }
+
+ // prepare bwrap pipe and args
+ if argsFD, _, err := b.p.prepareCmd(b.Cmd); err != nil {
+ return err
+ } else {
+ b.Cmd.Args = append(b.Cmd.Args, "--args", strconv.Itoa(argsFD), "--", b.name)
+ }
+
+ // prepare child args and pipes if enabled
+ if b.cp != nil {
+ b.cp.ready = ready
+ if argsFD, statFD, err := b.cp.prepareCmd(b.Cmd); err != nil {
+ return err
+ } else {
+ b.Cmd.Args = append(b.Cmd.Args, b.argF(argsFD, statFD)...)
+ }
+ } else {
+ b.Cmd.Args = append(b.Cmd.Args, b.argF(-1, -1)...)
+ }
+
+ if ready != nil {
+ b.Cmd.Env = append(b.Cmd.Env, FortifyHelper+"=1", FortifyStatus+"=1")
+ } else if b.cp != nil {
+ b.Cmd.Env = append(b.Cmd.Env, FortifyHelper+"=1", FortifyStatus+"=0")
+ } else {
+ b.Cmd.Env = append(b.Cmd.Env, FortifyHelper+"=1", FortifyStatus+"=-1")
+ }
+
+ if err := b.Cmd.Start(); err != nil {
+ return err
+ }
+
+ // write bwrap args first
+ if err := b.p.readyWriteArgs(); err != nil {
+ return err
+ }
+
+ // write child args if enabled
+ if b.cp != nil {
+ if err := b.cp.readyWriteArgs(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (b *bubblewrap) Close() error {
+ if b.cp == nil {
+ panic("attempted to close bwrap child initialised without pipes")
+ }
+
+ return b.cp.closeStatus()
+}
+
+func (b *bubblewrap) Start() error {
+ return b.StartNotify(nil)
+}
+
+func (b *bubblewrap) Unwrap() *exec.Cmd {
+ return b.Cmd
+}
+
+// MustNewBwrap initialises a new Bwrap instance with wt as the null-terminated argument writer.
+// If wt is nil, the child process spawned by bwrap will not get an argument pipe.
+// Function argF returns an array of arguments passed directly to the child process.
+func MustNewBwrap(conf *bwrap.Config, wt io.WriterTo, name string, argF func(argsFD, statFD int) []string) Helper {
+ b, err := NewBwrap(conf, wt, name, argF)
+ if err != nil {
+ panic(err.Error())
+ } else {
+ return b
+ }
+}
+
+// NewBwrap initialises a new Bwrap instance with wt as the null-terminated argument writer.
+// If wt is nil, the child process spawned by bwrap will not get an argument pipe.
+// Function argF returns an array of arguments passed directly to the child process.
+func NewBwrap(conf *bwrap.Config, wt io.WriterTo, name string, argF func(argsFD, statFD int) []string) (Helper, error) {
+ b := new(bubblewrap)
+
+ if args, err := NewCheckedArgs(conf.Args()); err != nil {
+ return nil, err
+ } else {
+ b.p = &pipes{args: args}
+ }
+
+ b.argF = argF
+ b.name = name
+ if wt != nil {
+ b.cp = &pipes{args: wt}
+ }
+ b.Cmd = execCommand(BubblewrapName)
+
+ return b, nil
+}