aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--helper/export_test.go18
-rw-r--r--helper/helper.go17
-rw-r--r--helper/stub_test.go75
3 files changed, 108 insertions, 2 deletions
diff --git a/helper/export_test.go b/helper/export_test.go
new file mode 100644
index 00000000..6bf18ce3
--- /dev/null
+++ b/helper/export_test.go
@@ -0,0 +1,18 @@
+package helper
+
+import (
+ "os"
+ "os/exec"
+ "testing"
+)
+
+// replace execCommand to have the resulting *exec.Cmd launch TestHelperChildStub
+func ReplaceExecCommand(t *testing.T) {
+ t.Cleanup(func() {
+ execCommand = exec.Command
+ })
+
+ execCommand = func(name string, arg ...string) *exec.Cmd {
+ return exec.Command(os.Args[0], append([]string{"-test.run=TestHelperChildStub", "--", name}, arg...)...)
+ }
+}
diff --git a/helper/helper.go b/helper/helper.go
index 5488ec50..14700920 100644
--- a/helper/helper.go
+++ b/helper/helper.go
@@ -16,6 +16,11 @@ var (
ErrStatusRead = errors.New("unexpected status response")
)
+const (
+ FortifyHelper = "FORTIFY_HELPER"
+ FortifyStatus = "FORTIFY_STATUS"
+)
+
// Helper wraps *exec.Cmd and manages status and args fd.
// Args is always 3 and status if set is always 4.
type Helper struct {
@@ -52,15 +57,20 @@ func (h *Helper) StartNotify(ready chan error) error {
h.argsP[0], h.argsP[1] = pr, pw
}
// create status pipes if ready signal is requested
+ var sv string
if ready != nil {
if pr, pw, err := os.Pipe(); err != nil {
return err
} else {
h.statP[0], h.statP[1] = pr, pw
}
+
+ sv = FortifyStatus + "=1"
+ } else {
+ sv = FortifyStatus + "=0"
}
- // prepare extra files
+ // prepare extra files from caller
el := len(h.ExtraFiles)
if ready != nil {
el += 2
@@ -76,6 +86,7 @@ func (h *Helper) StartNotify(ready chan error) error {
// prepare and start process
h.Cmd.ExtraFiles = ef
+ h.Cmd.Env = append(h.Cmd.Env, FortifyHelper+"=1", sv)
if err := h.Cmd.Start(); err != nil {
return err
}
@@ -175,10 +186,12 @@ func (h *Helper) Start() error {
return h.StartNotify(nil)
}
+var execCommand = exec.Command
+
func New(wt io.WriterTo, name string, arg ...string) *Helper {
if wt == nil {
panic("attempted to create helper with invalid argument writer")
}
- return &Helper{args: wt, Cmd: exec.Command(name, arg...)}
+ return &Helper{args: wt, Cmd: execCommand(name, arg...)}
}
diff --git a/helper/stub_test.go b/helper/stub_test.go
new file mode 100644
index 00000000..9f85ac65
--- /dev/null
+++ b/helper/stub_test.go
@@ -0,0 +1,75 @@
+package helper_test
+
+import (
+ "io"
+ "os"
+ "strconv"
+ "syscall"
+ "testing"
+
+ "git.ophivana.moe/cat/fortify/helper"
+)
+
+func TestHelperChildStub(t *testing.T) {
+ // this test mocks the helper process
+ if os.Getenv(helper.FortifyHelper) != "1" {
+ return
+ }
+ // simulate args pipe behaviour
+ func() {
+ f := os.NewFile(3, "|0")
+ if f == nil {
+ panic("attempted to start helper without args pipe")
+ }
+
+ if _, err := io.Copy(os.Stdout, f); err != nil {
+ panic("cannot read args: " + err.Error())
+ }
+ }()
+
+ var wait chan struct{}
+
+ // simulate status pipe behaviour
+ if os.Getenv(helper.FortifyStatus) == "1" {
+ wait = make(chan struct{})
+ go func() {
+ f := os.NewFile(4, "|1")
+ if f == nil {
+ panic("attempted to start with status reporting without status pipe")
+ }
+
+ if _, err := f.Write([]byte{'x'}); err != nil {
+ panic("cannot write to status pipe: " + err.Error())
+ }
+
+ // wait for status pipe close
+ var epoll int
+ if fd, err := syscall.EpollCreate1(0); err != nil {
+ panic("cannot open epoll fd: " + err.Error())
+ } else {
+ defer func() {
+ if err = syscall.Close(fd); err != nil {
+ panic("cannot close epoll fd: " + err.Error())
+ }
+ }()
+ epoll = fd
+ }
+ if err := syscall.EpollCtl(epoll, syscall.EPOLL_CTL_ADD, int(f.Fd()), &syscall.EpollEvent{}); err != nil {
+ panic("cannot add status pipe to epoll: " + err.Error())
+ }
+ events := make([]syscall.EpollEvent, 1)
+ if _, err := syscall.EpollWait(epoll, events, -1); err != nil {
+ panic("cannot poll status pipe: " + err.Error())
+ }
+ if events[0].Events != syscall.EPOLLERR {
+ panic(strconv.Itoa(int(events[0].Events)))
+
+ }
+ close(wait)
+ }()
+ }
+
+ if wait != nil {
+ <-wait
+ }
+}