zrepl/zfs/fork_reader.go
2017-05-13 15:23:37 +02:00

63 lines
1.2 KiB
Go

package zfs
import (
"bytes"
"fmt"
"io"
"os/exec"
)
// A ForkReader is an io.Reader for a forked process's stdout.
// It Wait()s for the process to exit and - if it exits with error - returns this exit error
// on subsequent Read()s.
type ForkExecReader struct {
Cmd *exec.Cmd
InStream io.Reader
StderrBuf *bytes.Buffer
}
func NewForkExecReader(command string, args ...string) (r *ForkExecReader, err error) {
r = &ForkExecReader{}
r.Cmd = exec.Command(command, args...)
r.InStream, err = r.Cmd.StdoutPipe()
if err != nil {
return
}
r.StderrBuf = bytes.NewBuffer(make([]byte, 0, 1024))
r.Cmd.Stderr = r.StderrBuf
if err = r.Cmd.Start(); err != nil {
return
}
return
}
type ForkExecReaderError struct {
WaitErr error
Stderr []byte
}
func (e ForkExecReaderError) Error() string {
return fmt.Sprintf("underlying process exited with error: %s\nstderr: %s\n", e.WaitErr, e.Stderr)
}
func (t *ForkExecReader) Read(buf []byte) (n int, err error) {
n, err = t.InStream.Read(buf)
if err == io.EOF {
waitErr := t.Cmd.Wait()
if waitErr != nil {
err = ForkExecReaderError{
WaitErr: waitErr,
Stderr: t.StderrBuf.Bytes(),
}
}
}
return
}