blob: e5fd73bdea46f6a89796c528f092f0fd3cf890ad [file] [log] [blame]
package modules
import (
"bufio"
"fmt"
"io"
"os"
"sync"
"syscall"
)
type pipe struct {
r, w *os.File
}
type functionHandle struct {
mu sync.Mutex
main Main
stdin, stderr, stdout pipe
bufferedStdout *bufio.Reader
}
func newFunctionHandle(main Main) command {
return &functionHandle{main: main}
}
func (fh *functionHandle) Stdout() *bufio.Reader {
fh.mu.Lock()
defer fh.mu.Unlock()
return fh.bufferedStdout
}
func (fh *functionHandle) Stderr() io.Reader {
fh.mu.Lock()
defer fh.mu.Unlock()
return fh.stderr.r
}
func (fh *functionHandle) Stdin() io.WriteCloser {
fh.mu.Lock()
defer fh.mu.Unlock()
return fh.stdin.w
}
func (fh *functionHandle) start(sh *Shell, args ...string) (Handle, error) {
fh.mu.Lock()
defer fh.mu.Unlock()
for _, p := range []*pipe{&fh.stdin, &fh.stdout, &fh.stderr} {
var err error
if p.r, p.w, err = os.Pipe(); err != nil {
return nil, err
}
}
fh.bufferedStdout = bufio.NewReader(fh.stdout.r)
go func() {
err := fh.main(fh.stdin.r, fh.stdout.w, fh.stderr.w, sh.mergeOSEnv(), args...)
if err != nil {
fmt.Fprintf(fh.stderr.w, "%s\n", err)
}
// See the comment below in Shutdown.
syscall.Close(int(fh.stdin.r.Fd()))
syscall.Close(int(fh.stdout.w.Fd()))
syscall.Close(int(fh.stderr.w.Fd()))
}()
return fh, nil
}
func (fh *functionHandle) Shutdown(output io.Writer) {
fh.mu.Lock()
defer fh.mu.Unlock()
scanner := bufio.NewScanner(fh.stderr.r)
for scanner.Scan() {
fmt.Fprintf(output, "%s\n", scanner.Text())
}
// We close these files using the Close system call since there
// may be an oustanding read on them that would otherwise trigger
// a test failure with go test -race
syscall.Close(int(fh.stdin.w.Fd()))
syscall.Close(int(fh.stdout.r.Fd()))
syscall.Close(int(fh.stderr.r.Fd()))
}