From 7d237fd73b611f5b9721a27b5382daf4ee64fcbd Mon Sep 17 00:00:00 2001 From: Mohammed Al Sahaf Date: Sat, 23 Nov 2019 00:37:53 +0300 Subject: [PATCH] retracing steps by re-adding syscallDup along with variations for more GOOS --- internal/remote/output_interceptor.go | 77 ++++++++++------------ internal/remote/syscall_dup_linux_arm64.go | 11 ++++ internal/remote/syscall_dup_plan9.go | 14 ++++ internal/remote/syscall_dup_solaris.go | 9 +++ internal/remote/syscall_dup_unix.go | 12 ++++ internal/remote/syscall_dup_windows.go | 22 +++++++ 6 files changed, 102 insertions(+), 43 deletions(-) create mode 100644 internal/remote/syscall_dup_linux_arm64.go create mode 100644 internal/remote/syscall_dup_plan9.go create mode 100644 internal/remote/syscall_dup_solaris.go create mode 100644 internal/remote/syscall_dup_unix.go create mode 100644 internal/remote/syscall_dup_windows.go diff --git a/internal/remote/output_interceptor.go b/internal/remote/output_interceptor.go index 331db1ad4..81d5b940c 100644 --- a/internal/remote/output_interceptor.go +++ b/internal/remote/output_interceptor.go @@ -1,7 +1,6 @@ package remote import ( - "bytes" "errors" "io" "io/ioutil" @@ -19,46 +18,45 @@ type OutputInterceptor interface { } func NewOutputInterceptor() OutputInterceptor { - return &outputInterceptor{buffer: &bytes.Buffer{}} + return &outputInterceptor{} } type outputInterceptor struct { - origStdout *os.File - origStderr *os.File - readSources [2]io.ReadCloser // stores the reader pipes form os.Pipe() for closure. - streamTarget *os.File - combinedReader io.Reader - intercepting bool - tailer io.Reader - buffer *bytes.Buffer + redirectFile *os.File + streamTarget *os.File + intercepting bool + tailer io.Reader } func (interceptor *outputInterceptor) StartInterceptingOutput() error { if interceptor.intercepting { return errors.New("Already intercepting output!") } - interceptor.origStdout = os.Stdout - interceptor.origStderr = os.Stderr - stdoutRead, stdoutWrite, err := os.Pipe() + interceptor.intercepting = true + + var err error + interceptor.redirectFile, err = ioutil.TempFile("", "ginkgo-output") if err != nil { return err } - stderrRead, stderrWrite, err := os.Pipe() - if err != nil { + + // Call a function in ./syscall_dup_*.go + // If building for plan9, use Dup. If building for Windows, use SetStdHandle. If building everything + // other than linux_arm64 or plan9 or Windows, use a "normal" syscall.Dup2(oldfd, newfd) call. + // If building for linux_arm64 (which doesn't have syscall.Dup2), call syscall.Dup3(oldfd, newfd, 0). + // They are nearly identical, see: http://linux.die.net/man/2/dup3 + if err := syscallDup(int(interceptor.redirectFile.Fd()), 1); err != nil { + os.Remove(interceptor.redirectFile.Name()) + return err + } + if err := syscallDup(int(interceptor.redirectFile.Fd()), 2); err != nil { + os.Remove(interceptor.redirectFile.Name()) return err } - os.Stdout = stdoutWrite - os.Stderr = stderrWrite - - interceptor.intercepting = true - - interceptor.readSources = [2]io.ReadCloser{stderrRead, stderrRead} - interceptor.combinedReader = io.TeeReader(io.MultiReader(stderrRead, stdoutRead), interceptor.buffer) - - // if interceptor.streamTarget != nil { - // interceptor.tailer = io.TeeReader(interceptor.combinedReader, interceptor.streamTarget) - // } + if interceptor.streamTarget != nil { + interceptor.tailer = io.TeeReader(interceptor.redirectFile, interceptor.streamTarget) + } return nil } @@ -68,28 +66,21 @@ func (interceptor *outputInterceptor) StopInterceptingAndReturnOutput() (string, return "", errors.New("Not intercepting output!") } - output, err := ioutil.ReadAll(interceptor.buffer) - - currStdout := os.Stdout - currStdout.Close() - currStderr := os.Stderr - currStderr.Close() + interceptor.redirectFile.Close() + output, err := ioutil.ReadFile(interceptor.redirectFile.Name()) + //os.Remove(interceptor.redirectFile.Name()) - os.Stdout = interceptor.origStdout - os.Stderr = interceptor.origStderr + interceptor.intercepting = false - for _, r := range interceptor.readSources { - if closerErr := r.Close(); closerErr != nil { - err = closerErr + if interceptor.streamTarget != nil { + // reading the redirectFile causes the io.TeeReader to write to streamTarget, + // so we just need to sync. + er := interceptor.streamTarget.Sync() + if er != nil { + err = er } } - interceptor.intercepting = false - - // if interceptor.streamTarget != nil { - // interceptor.streamTarget.Sync() - // } - return string(output), err } diff --git a/internal/remote/syscall_dup_linux_arm64.go b/internal/remote/syscall_dup_linux_arm64.go new file mode 100644 index 000000000..9550d37b3 --- /dev/null +++ b/internal/remote/syscall_dup_linux_arm64.go @@ -0,0 +1,11 @@ +// +build linux,arm64 + +package remote + +import "syscall" + +// linux_arm64 doesn't have syscall.Dup2 which ginkgo uses, so +// use the nearly identical syscall.Dup3 instead +func syscallDup(oldfd int, newfd int) (err error) { + return syscall.Dup3(oldfd, newfd, 0) +} diff --git a/internal/remote/syscall_dup_plan9.go b/internal/remote/syscall_dup_plan9.go new file mode 100644 index 000000000..93fd4068f --- /dev/null +++ b/internal/remote/syscall_dup_plan9.go @@ -0,0 +1,14 @@ +// +build plan9 + +package remote + +import ( + "syscall" +) + +// Plan9 doesn't have syscall.Dup2 which ginkgo uses, so +// use the identical syscall.Dup instead +func syscallDup(oldfd int, newfd int) (err error) { + _, err := syscall.Dup(oldfd, newfd, 0) + return err +} diff --git a/internal/remote/syscall_dup_solaris.go b/internal/remote/syscall_dup_solaris.go new file mode 100644 index 000000000..75ef7fb78 --- /dev/null +++ b/internal/remote/syscall_dup_solaris.go @@ -0,0 +1,9 @@ +// +build solaris + +package remote + +import "golang.org/x/sys/unix" + +func syscallDup(oldfd int, newfd int) (err error) { + return unix.Dup2(oldfd, newfd) +} diff --git a/internal/remote/syscall_dup_unix.go b/internal/remote/syscall_dup_unix.go new file mode 100644 index 000000000..7c489e96c --- /dev/null +++ b/internal/remote/syscall_dup_unix.go @@ -0,0 +1,12 @@ +// +build !linux !arm64 +// +build !windows +// +build !solaris +// +build !plan9 + +package remote + +import "syscall" + +func syscallDup(oldfd int, newfd int) (err error) { + return syscall.Dup2(oldfd, newfd) +} diff --git a/internal/remote/syscall_dup_windows.go b/internal/remote/syscall_dup_windows.go new file mode 100644 index 000000000..0c40ae4dd --- /dev/null +++ b/internal/remote/syscall_dup_windows.go @@ -0,0 +1,22 @@ +// +build windows + +package remote + +import ( + "errors" + + "golang.org/x/sys/windows" +) + +func syscallDup(oldfd int, newfd int) (err error) { + var stdfd uint32 + switch newfd { + case 1: + stdfd = windows.STD_OUTPUT_HANDLE + case 2: + stdfd = windows.STD_ERROR_HANDLE + default: + return errors.New("unrecognized newfd: %d", newfd) + } + return windows.SetStdHandle(stdfd, windows.Handle(oldfd)) +}