diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-02-13 23:15:34 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-02-13 23:34:15 +0900 |
| commit | fe7d208cf76fa6f24bb9d12ba29b5ed61d837ce3 (patch) | |
| tree | cac02af50a13b2078739a8f5a74d219f3b60833d /helper/helper.go | |
| parent | 60c287375048b21eab2bd82f1e7d43e36dcfb3a2 (diff) | |
helper: use generic extra files interface
This replaces the pipes object and integrates context into helper process lifecycle.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'helper/helper.go')
| -rw-r--r-- | helper/helper.go | 130 |
1 files changed, 114 insertions, 16 deletions
diff --git a/helper/helper.go b/helper/helper.go index d3115c59..2ef1afaf 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -2,35 +2,133 @@ package helper import ( - "errors" + "context" + "fmt" + "io" + "os" "os/exec" + "slices" + "syscall" + "time" + + "git.gensokyo.uk/security/fortify/helper/proc" ) var ( - ErrStatusFault = errors.New("generic status pipe fault") - ErrStatusRead = errors.New("unexpected status response") + WaitDelay = 2 * time.Second ) const ( - // FortifyHelper is set for the process launched by Helper. + // FortifyHelper is set to 1 when args fd is enabled and 0 otherwise. FortifyHelper = "FORTIFY_HELPER" - // FortifyStatus is 1 when sync fd is enabled and 0 otherwise. + // FortifyStatus is set to 1 when stat fd is enabled and 0 otherwise. FortifyStatus = "FORTIFY_STATUS" ) type Helper interface { - // StartNotify starts the helper process. - // A status pipe is passed to the helper if ready is not nil. - StartNotify(ready chan error) error + // Stdin sets the standard input of Helper. + Stdin(r io.Reader) Helper + // Stdout sets the standard output of Helper. + Stdout(w io.Writer) Helper + // Stderr sets the standard error of Helper. + Stderr(w io.Writer) Helper + // SetEnv sets the environment of Helper. + SetEnv(env []string) Helper + // Start starts the helper process. - Start() error - // Close closes the status pipe. - // If helper is started without the status pipe, Close panics. - Close() error - // Wait calls wait on the child process and cleans up pipes. + // A status pipe is passed to the helper if stat is true. + Start(ctx context.Context, stat bool) error + // Wait blocks until Helper exits and releases all its resources. Wait() error - // Unwrap returns the underlying exec.Cmd instance. - Unwrap() *exec.Cmd + + fmt.Stringer +} + +func newHelperCmd( + h Helper, name string, + wt io.WriterTo, argF func(argsFd, statFd int) []string, + extraFiles []*os.File, +) (cmd *helperCmd) { + cmd = new(helperCmd) + + cmd.r = h + cmd.name = name + + cmd.extraFiles = new(proc.ExtraFilesPre) + for _, f := range extraFiles { + _, v := cmd.extraFiles.Append() + *v = f + } + + argsFd := -1 + if wt != nil { + f := proc.NewWriterTo(wt) + argsFd = int(proc.InitFile(f, cmd.extraFiles)) + cmd.files = append(cmd.files, f) + cmd.hasArgsFd = true + } + cmd.argF = func(statFd int) []string { return argF(argsFd, statFd) } + + return +} + +// helperCmd wraps Cmd and implements methods shared across all Helper implementations. +type helperCmd struct { + // ref to parent + r Helper + + // returns an array of arguments passed directly + // to the helper process + argF func(statFd int) []string + // whether argsFd is present + hasArgsFd bool + + // closes statFd + stat io.Closer + // deferred extraFiles fulfillment + files []proc.File + // passed through to [proc.Fulfill] and [proc.InitFile] + extraFiles *proc.ExtraFilesPre + + name string + stdin io.Reader + stdout, stderr io.Writer + env []string + *exec.Cmd +} + +func (h *helperCmd) Stdin(r io.Reader) Helper { h.stdin = r; return h.r } +func (h *helperCmd) Stdout(w io.Writer) Helper { h.stdout = w; return h.r } +func (h *helperCmd) Stderr(w io.Writer) Helper { h.stderr = w; return h.r } +func (h *helperCmd) SetEnv(env []string) Helper { h.env = env; return h.r } + +// finalise initialises the underlying [exec.Cmd] object. +func (h *helperCmd) finalise(ctx context.Context, stat bool) (args []string) { + h.Cmd = commandContext(ctx, h.name) + h.Cmd.Stdin, h.Cmd.Stdout, h.Cmd.Stderr = h.stdin, h.stdout, h.stderr + h.Cmd.Env = slices.Grow(h.env, 2) + if h.hasArgsFd { + h.Cmd.Env = append(h.Cmd.Env, FortifyHelper+"=1") + } else { + h.Cmd.Env = append(h.Cmd.Env, FortifyHelper+"=0") + } + + h.Cmd.Cancel = func() error { return h.Cmd.Process.Signal(syscall.SIGTERM) } + h.Cmd.WaitDelay = WaitDelay + + statFd := -1 + if stat { + f := proc.NewStat(&h.stat) + statFd = int(proc.InitFile(f, h.extraFiles)) + h.files = append(h.files, f) + h.Cmd.Env = append(h.Cmd.Env, FortifyStatus+"=1") + + // stat is populated on fulfill + h.Cmd.Cancel = func() error { return h.stat.Close() } + } else { + h.Cmd.Env = append(h.Cmd.Env, FortifyStatus+"=0") + } + return h.argF(statFd) } -var execCommand = exec.Command +var commandContext = exec.CommandContext |
