diff options
| author | Ophestra Umiker <cat@ophivana.moe> | 2024-10-08 18:02:38 +0900 |
|---|---|---|
| committer | Ophestra Umiker <cat@ophivana.moe> | 2024-10-08 18:02:38 +0900 |
| commit | 7c7999e9e527bc245560ea6804a867276acaae6f (patch) | |
| tree | 903be2bec90b79b9202f91ec4cede8c869abd4fb /helper/bwrap.go | |
| parent | c6223771dbc9cf8a4a44424759591adb496f2ce5 (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.go | 144 |
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 +} |
