From 73c1a830320636f91a0d15d6d247e88650e81df1 Mon Sep 17 00:00:00 2001
From: Ophestra <cat@gensokyo.uk>
Date: Sat, 15 Mar 2025 00:33:25 +0900
Subject: [PATCH] helper: move process wrapper to direct

Signed-off-by: Ophestra <cat@gensokyo.uk>
---
 helper/direct.go | 109 ++++++++++++++++++++++++++++++++++++++++-------
 helper/helper.go |  87 +------------------------------------
 2 files changed, 96 insertions(+), 100 deletions(-)

diff --git a/helper/direct.go b/helper/direct.go
index 93eae22..16aede8 100644
--- a/helper/direct.go
+++ b/helper/direct.go
@@ -4,12 +4,33 @@ import (
 	"context"
 	"errors"
 	"io"
+	"os"
 	"os/exec"
+	"slices"
 	"sync"
+	"syscall"
 
 	"git.gensokyo.uk/security/fortify/helper/proc"
 )
 
+// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
+// Function argF returns an array of arguments passed directly to the child process.
+func NewDirect(
+	ctx context.Context,
+	wt io.WriterTo,
+	name string,
+	argF func(argsFd, statFd int) []string,
+	cmdF func(cmd *exec.Cmd),
+	stat bool,
+) Helper {
+	d := new(direct)
+	d.helperCmd = newHelperCmd(ctx, name, wt, argF, nil, stat)
+	if cmdF != nil {
+		cmdF(d.helperCmd.Cmd)
+	}
+	return d
+}
+
 // direct starts the helper directly and manages status and args fd.
 type direct struct {
 	lock sync.RWMutex
@@ -31,20 +52,78 @@ func (h *direct) Start() error {
 	return proc.Fulfill(h.ctx, &h.ExtraFiles, h.Cmd.Start, h.files, h.extraFiles)
 }
 
-// NewDirect initialises a new direct Helper instance with wt as the null-terminated argument writer.
-// Function argF returns an array of arguments passed directly to the child process.
-func NewDirect(
-	ctx context.Context,
-	wt io.WriterTo,
-	name string,
-	argF func(argsFd, statFd int) []string,
-	cmdF func(cmd *exec.Cmd),
-	stat bool,
-) Helper {
-	d := new(direct)
-	d.helperCmd = newHelperCmd(ctx, name, wt, argF, nil, stat)
-	if cmdF != nil {
-		cmdF(d.helperCmd.Cmd)
+func newHelperCmd(
+	ctx context.Context, name string,
+	wt io.WriterTo, argF func(argsFd, statFd int) []string,
+	extraFiles []*os.File, stat bool,
+) (cmd *helperCmd) {
+	cmd = new(helperCmd)
+	cmd.ctx = ctx
+	cmd.hasStatFd = stat
+
+	cmd.Cmd = commandContext(ctx, name)
+	cmd.Cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGTERM) }
+	cmd.WaitDelay = WaitDelay
+
+	cmd.extraFiles = new(proc.ExtraFilesPre)
+	for _, f := range extraFiles {
+		_, v := cmd.extraFiles.Append()
+		*v = f
 	}
-	return d
+
+	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 {
+	// returns an array of arguments passed directly
+	// to the helper process
+	argF func(statFd int) []string
+	// whether argsFd is present
+	hasArgsFd bool
+	// whether statFd is present
+	hasStatFd bool
+
+	// closes statFd
+	stat io.Closer
+	// deferred extraFiles fulfillment
+	files []proc.File
+	// passed through to [proc.Fulfill] and [proc.InitFile]
+	extraFiles *proc.ExtraFilesPre
+
+	ctx context.Context
+	*exec.Cmd
+}
+
+// finalise sets up the underlying [exec.Cmd] object.
+func (h *helperCmd) finalise() (args []string) {
+	h.Env = slices.Grow(h.Env, 2)
+	if h.hasArgsFd {
+		h.Cmd.Env = append(h.Env, FortifyHelper+"=1")
+	} else {
+		h.Cmd.Env = append(h.Env, FortifyHelper+"=0")
+	}
+
+	statFd := -1
+	if h.hasStatFd {
+		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)
 }
diff --git a/helper/helper.go b/helper/helper.go
index c500c63..ab27644 100644
--- a/helper/helper.go
+++ b/helper/helper.go
@@ -2,20 +2,15 @@
 package helper
 
 import (
-	"context"
 	"fmt"
-	"io"
-	"os"
 	"os/exec"
-	"slices"
-	"syscall"
 	"time"
-
-	"git.gensokyo.uk/security/fortify/helper/proc"
 )
 
 var (
 	WaitDelay = 2 * time.Second
+
+	commandContext = exec.CommandContext
 )
 
 const (
@@ -33,81 +28,3 @@ type Helper interface {
 
 	fmt.Stringer
 }
-
-func newHelperCmd(
-	ctx context.Context, name string,
-	wt io.WriterTo, argF func(argsFd, statFd int) []string,
-	extraFiles []*os.File, stat bool,
-) (cmd *helperCmd) {
-	cmd = new(helperCmd)
-	cmd.ctx = ctx
-	cmd.hasStatFd = stat
-
-	cmd.Cmd = commandContext(ctx, name)
-	cmd.Cmd.Cancel = func() error { return cmd.Process.Signal(syscall.SIGTERM) }
-	cmd.WaitDelay = WaitDelay
-
-	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 {
-	// returns an array of arguments passed directly
-	// to the helper process
-	argF func(statFd int) []string
-	// whether argsFd is present
-	hasArgsFd bool
-	// whether statFd is present
-	hasStatFd bool
-
-	// closes statFd
-	stat io.Closer
-	// deferred extraFiles fulfillment
-	files []proc.File
-	// passed through to [proc.Fulfill] and [proc.InitFile]
-	extraFiles *proc.ExtraFilesPre
-
-	ctx context.Context
-	*exec.Cmd
-}
-
-// finalise sets up the underlying [exec.Cmd] object.
-func (h *helperCmd) finalise() (args []string) {
-	h.Env = slices.Grow(h.Env, 2)
-	if h.hasArgsFd {
-		h.Cmd.Env = append(h.Env, FortifyHelper+"=1")
-	} else {
-		h.Cmd.Env = append(h.Env, FortifyHelper+"=0")
-	}
-
-	statFd := -1
-	if h.hasStatFd {
-		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 commandContext = exec.CommandContext