Example #1
1
func start(c *exec.Cmd) (io.ReadCloser, io.WriteCloser, error) {
	c.SysProcAttr = &syscall.SysProcAttr{}
	//c.SysProcAttr.CreationFlags = 16 // CREATE_NEW_CONSOLE

	stdin, err := c.StdinPipe()

	if err != nil {
		return nil, nil, err
	}

	stdout, err := c.StdoutPipe()

	if err != nil {
		return nil, nil, err
	}

	stderr, err := c.StderrPipe()

	if err != nil {
		return nil, nil, err
	}

	t := &term{}
	t.cmd = c
	t.stderr = stderr
	t.stdout = stdout
	t.stdin = stdin

	err = t.start()
	if err != nil {
		return nil, nil, err
	}

	return t, t, nil
}
func (sess *session) runWithPty(command *exec.Cmd) error {
	logger := sess.logger.Session("run")

	command.Stdout = sess.channel
	command.Stderr = sess.channel.Stderr()

	stdin, err := command.StdinPipe()
	if err != nil {
		return err
	}

	go func() {
		for {
			mybuffer := make([]byte, 255)
			n, err := sess.channel.Read(mybuffer)
			if err == nil {
				inp := mybuffer[0:n]
				logger.Info("stdin", lager.Data{"buffer": inp})
				stdin.Write(inp)
				if string(inp) == "\r" {
					stdin.Write([]byte("\r\n"))
				}
			}
		}
	}()

	return sess.runner.Start(command)
}
Example #3
1
func Sudo(user string, cmdStr string, shell string, input *[]byte) (*SudoResult, *Error) {
	var cmd *exec.Cmd = sudo_cmd(user, cmdStr, shell)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, &Error{"Failed to get pipe to stdout.", err}
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, &Error{"Failed to get pipe to stderr.", err}
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, &Error{"Failed to get pipe to stdin.", err}
	}

	// start cmd
	if err := cmd.Start(); err != nil {
		return nil, &Error{"Failed to execute command \"" + cmdStr + "\".", err}
	}

	if input != nil {
		// write input
		stdin.Write(*input)
	}
	stdin.Close()

	// read output
	stdoutBytes, err := ioutil.ReadAll(stdout)
	if err != nil {
		return nil, &Error{"Failed to read stdout.", err}
	}
	stderrBytes, err := ioutil.ReadAll(stderr)
	if err != nil {
		return nil, &Error{"Failed to read stderr.", err}
	}

	// finish execution
	err = cmd.Wait()
	if err != nil {
		ErrLogger.Printf("sudo: %v", err)
		_, flag := err.(*exec.ExitError)
		if !flag {
			return nil, &Error{"Failed to execute command \"" + cmdStr + "\".", err}
		}
	}

	// return result
	res := &SudoResult{}
	res.Stdout = stdoutBytes
	res.Stderr = stderrBytes
	res.Succeeded = (err == nil)
	return res, nil
}
Example #4
0
func (c *ClientServer) preprocessFile(cmd *exec.Cmd, f io.Reader) ([]byte, error) {
	if stdout, err := cmd.StdoutPipe(); err == nil {
		if stdin, err := cmd.StdinPipe(); err == nil {
			if stderr, err := cmd.StderrPipe(); err == nil {
				if err := cmd.Start(); err == nil {
					if b, err := ioutil.ReadAll(f); err == nil {
						stdin.Write(b)
						stdin.Close()
						out, _ := ioutil.ReadAll(stdout)
						stde, _ := ioutil.ReadAll(stderr)
						if e := cmd.Wait(); e == nil {
							return out, nil
						} else {
							return stde, fmt.Errorf("Preprocessor failed.")
						}
					} else {
						return nil, err
					}
				} else {
					return nil, err
				}
			} else {
				return nil, err
			}
		} else {
			return nil, err
		}
	} else {
		return nil, err
	}
}
// Publish prints the result of a check to stdout.
func (p *ExecPublisher) Publish(result *CheckResult) error {
	var cmd *exec.Cmd
	if len(p.cmd) > 1 {
		cmd = exec.Command(p.cmd[0], p.cmd[1:]...)
	} else {
		cmd = exec.Command(p.cmd[0])
	}
	var b bytes.Buffer
	cmd.Stdout = &b
	cmd.Stderr = &b
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}
	if err = cmd.Start(); err != nil {
		p.log.Error("Exec failed", "error", err)
		return err
	}
	if err = p.stdinTemplate.Execute(stdin, result); err != nil {
		p.log.Error("Failed to write template data to stdin", "error", err)
		return err
	}
	stdin.Close()
	if err = cmd.Wait(); err != nil {
		p.log.Error("Exec failed", "error", err, "output", b.String())
		return err
	}
	return nil
}
Example #6
0
func attachCmd(cmd *exec.Cmd, ch ssh.Channel) (*sync.WaitGroup, error) {
	var wg sync.WaitGroup
	wg.Add(3)

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	go func() {
		defer wg.Done()
		io.Copy(stdin, ch)
	}()

	go func() {
		defer wg.Done()
		io.Copy(ch.Stderr(), stderr)
	}()

	go func() {
		defer wg.Done()
		io.Copy(ch, stdout)
	}()

	return &wg, nil
}
Example #7
0
func execCommand(_result map[string]int, _cmd []string) {
	var r ResultSlice

	for k, v := range _result {
		r.Results = append(r.Results, Result{Host: k, Count: v})
	}

	b, err := json.Marshal(r)
	HandleError(err)

	var cmd *exec.Cmd
	if len(_cmd) == 0 {
		cmd = exec.Command(_cmd[0])
	} else {
		cmd = exec.Command(_cmd[0], _cmd[1:]...)
	}

	stdin, err := cmd.StdinPipe()
	HandleError(err)

	io.WriteString(stdin, string(b))

	stdin.Close()

	out, err := cmd.CombinedOutput()
	HandleError(err)

	fmt.Println(string(out))
}
Example #8
0
func WrapinMKV(uuidpath string, audioin chan []byte, audio bool) {
	var ffmpeg *exec.Cmd
	if audio {
		ffmpeg = exec.Command("ffmpeg", "-f", "mjpeg", "-i", uuidpath, "-f", "s32be", "-ac", "2", "-ar", "44100", "-i", "pipe:0", "-f", "matroska", "-codec", "copy", "pipe:1")
	} else {
		ffmpeg = exec.Command("ffmpeg", "-f", "mjpeg", "-i", uuidpath, "-f", "matroska", "-codec", "copy", "pipe:1")
	}
	ffmpegstdout, err := ffmpeg.StdoutPipe()
	if err != nil {
		log.Fatalf("Unable to setup pipes for ffmpeg (stdout)")
	}
	ffmpeg.Stderr = os.Stderr

	audiofile, err := ffmpeg.StdinPipe()

	go DumpChanToFile(audioin, audiofile)

	ffmpeg.Start()

	for {
		_, err := io.Copy(os.Stdout, ffmpegstdout)
		if err != nil {
			log.Fatalf("unable to read to stdout: %s", err.Error())
		}
	}
}
Example #9
0
func runPlayer(command string, msgPath string) (*exec.Cmd, io.WriteCloser, io.ReadCloser, error) {
	if argv := strings.Fields(command); len(argv) == 0 {
		return nil, nil, nil, os.ErrInvalid
	} else if name, err := exec.LookPath(argv[0]); err != nil {
		return nil, nil, nil, err
	} else if dir, err := os.Getwd(); err != nil {
		return nil, nil, nil, err
	} else {
		cmd := exec.Cmd{Path: name, Args: argv, Dir: dir}
		if stdin, err := cmd.StdinPipe(); err != nil {
			return nil, nil, nil, err
		} else if stdout, err := cmd.StdoutPipe(); err != nil {
			return nil, nil, nil, err
		} else {
			if msgPath == "-" {
				cmd.Stderr = os.Stderr
			} else if msgPath != "" {
				if w, err := os.Create(msgPath); err != nil {
					// Connect to stderr instead
					fmt.Fprintln(os.Stderr, err)
					cmd.Stderr = os.Stderr
				} else {
					cmd.Stderr = w
				}
			}
			if err := cmd.Start(); err != nil {
				return nil, nil, nil, err
			}
			return &cmd, stdin, stdout, nil
		}
	}
}
Example #10
0
func attachCmd(cmd *exec.Cmd, stdout, stderr io.Writer, stdin io.Reader) chan error {
	errCh := make(chan error)

	stdinIn, err := cmd.StdinPipe()
	if err != nil {
		panic(err)
	}
	stdoutOut, err := cmd.StdoutPipe()
	if err != nil {
		panic(err)
	}
	stderrOut, err := cmd.StderrPipe()
	if err != nil {
		panic(err)
	}

	go func() {
		_, e := io.Copy(stdinIn, stdin)
		errCh <- e
	}()
	go func() {
		_, e := io.Copy(stdout, stdoutOut)
		errCh <- e
	}()
	go func() {
		_, e := io.Copy(stderr, stderrOut)
		errCh <- e
	}()

	return errCh
}
Example #11
0
func rsyncWebsocket(cmd *exec.Cmd, conn *websocket.Conn) error {
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return err
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	shared.WebsocketMirror(conn, stdin, stdout)
	data, err2 := ioutil.ReadAll(stderr)
	if err2 != nil {
		shared.Debugf("error reading rsync stderr: %s", err2)
		return err2
	}
	shared.Debugf("Stderr from rsync: %s", data)

	err = cmd.Wait()
	if err != nil {
		shared.Debugf("rsync recv error %s: %s", err, string(data))
	}

	return err
}
Example #12
0
func (c *Communicator) Start(remote *packer.RemoteCmd) error {
	var cmd *exec.Cmd
	if c.Config.Pty {
		cmd = exec.Command("docker", "exec", "-i", "-t", c.ContainerId, "/bin/sh", "-c", fmt.Sprintf("(%s)", remote.Command))
	} else {
		cmd = exec.Command("docker", "exec", "-i", c.ContainerId, "/bin/sh", "-c", fmt.Sprintf("(%s)", remote.Command))
	}

	var (
		stdin_w io.WriteCloser
		err     error
	)

	stdin_w, err = cmd.StdinPipe()
	if err != nil {
		return err
	}

	stderr_r, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	stdout_r, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	// Run the actual command in a goroutine so that Start doesn't block
	go c.run(cmd, remote, stdin_w, stdout_r, stderr_r)

	return nil
}
Example #13
0
func attachCmd(cmd *exec.Cmd, stdout, stderr io.Writer, stdin io.Reader) (*sync.WaitGroup, error) {
	var wg sync.WaitGroup
	wg.Add(2)

	stdinIn, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}
	stdoutOut, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderrOut, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}

	go func() {
		io.Copy(stdinIn, stdin)
		stdinIn.Close()
	}()
	go func() {
		io.Copy(stdout, stdoutOut)
		wg.Done()
	}()
	go func() {
		io.Copy(stderr, stderrOut)
		wg.Done()
	}()

	return &wg, nil
}
Example #14
0
func wrapStdin(proc *exec.Cmd, stdin io.Reader, done chan bool) {
	logger.Println("Wrapping stdin")

	pipe, err := proc.StdinPipe()
	fatal_if(err)

	go inLoop(pipe, stdin, done)
}
Example #15
0
func sudo(user string, cmdStr string, shell string, input *string) (*SudoResult, *JobberError) {
	var cmd *exec.Cmd = exec.Command("su",
		"--login", // login shell
		"--shell", shell,
		"--command", cmdStr,
		user)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, &JobberError{"Failed to get pipe to stdout.", err}
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, &JobberError{"Failed to get pipe to stderr.", err}
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, &JobberError{"Failed to get pipe to stdin.", err}
	}

	// start cmd
	if err := cmd.Start(); err != nil {
		return nil, &JobberError{"Failed to execute command \"" + cmdStr + "\".", err}
	}

	if input != nil {
		// write input
		stdin.Write([]byte(*input))
	}
	stdin.Close()

	// read output
	stdoutBytes, err := ioutil.ReadAll(stdout)
	if err != nil {
		return nil, &JobberError{"Failed to read stdout.", err}
	}
	stdoutStr := string(stdoutBytes)
	stderrBytes, err := ioutil.ReadAll(stderr)
	if err != nil {
		return nil, &JobberError{"Failed to read stderr.", err}
	}
	stderrStr := string(stderrBytes)

	// finish execution
	err = cmd.Wait()
	if err != nil {
		_, flag := err.(*exec.ExitError)
		if !flag {
			return nil, &JobberError{"Failed to execute command \"" + cmdStr + "\".", err}
		}
	}

	// return result
	res := &SudoResult{}
	res.Stdout = stdoutStr
	res.Stderr = stderrStr
	res.Succeeded = (err == nil)
	return res, nil
}
Example #16
0
func RunPipe(src, dst *exec.Cmd) (int64, error) {
	if src.Stdout != nil {
		return 0, fmt.Errorf("RunPipe: stdout already set on source")
	}
	if dst.Stdin != nil {
		return 0, fmt.Errorf("RunPipe: stdin already set on dest")
	}
	log.Spamf("RunPipe: src=%q  dst=%q", src.Path, dst.Path)

	pr, err := src.StdoutPipe()
	if err != nil {
		return 0, err
	}
	pw, err := dst.StdinPipe()
	if err != nil {
		return 0, err
	}

	log.Spam("RunPipe: starting src")
	err = src.Start()
	if err != nil {
		log.Spam("RunPipe: error starting src: ", err)
		return 0, err
	}

	log.Spam("RunPipe: starting dst")
	err = dst.Start()
	if err != nil {
		log.Spam("RunPipe: error starting dst: ", err)
		log.Spam("RunPipe: waiting for src to die")
		pr.Close()
		src.Wait()
		return 0, err
	}

	log.Spam("RunPipe: copying data")
	copied, copyErr := io.Copy(pw, pr)
	if copyErr == io.EOF {
		copyErr = nil // EOF isn't really an error in this case
	}
	log.Spamf("RunPipe: copied %d bytes, err: %v", copied, copyErr)

	// close all pipes and let everything die
	log.Spam("RunPipe: waiting for all children to die")
	closeErr1, closeErr2 := pr.Close(), pw.Close()
	waitErr1, waitErr2 := src.Wait(), dst.Wait()

	// and finally, figure out resulting error code
	errs := []error{copyErr, waitErr1, waitErr2, closeErr1, closeErr2}
	for _, e := range errs {
		if e != nil {
			return copied, e
		}
	}
	return copied, nil
}
Example #17
0
func runCommandStdinPipe(cmd *exec.Cmd) (pipe io.WriteCloser) {
	pipe, err := cmd.StdinPipe()
	if err != nil {
		fail(fmt.Sprintf("Unable to create pipe to command: %v\n", err))
	}
	err = cmd.Start()
	if err != nil {
		fail(fmt.Sprintf("Unable to run pipe to command: %v\n", err))
	}

	return
}
Example #18
0
func spawn(cmd *exec.Cmd, ttySpec *garden.TTYSpec, stdout io.Writer, stderr io.Writer) (process, io.WriteCloser, error) {
	var stdin io.WriteCloser
	var err error

	var processPty *os.File

	if ttySpec != nil {
		pty, tty, err := pty.Open()
		if err != nil {
			return nil, nil, err
		}

		// close our end of the tty after the process has spawned
		defer tty.Close()

		processPty = pty
		stdin = pty

		windowColumns := 80
		windowRows := 24
		if ttySpec.WindowSize != nil {
			windowColumns = ttySpec.WindowSize.Columns
			windowRows = ttySpec.WindowSize.Rows
		}

		ptyutil.SetWinSize(pty, windowColumns, windowRows)

		cmd.Stdin = tty
		cmd.Stdout = tty
		cmd.Stderr = tty

		go io.Copy(stdout, pty)
	} else {
		stdin, err = cmd.StdinPipe()
		if err != nil {
			return nil, nil, err
		}

		cmd.Stdout = stdout
		cmd.Stderr = stderr
	}

	err = cmd.Start()
	if err != nil {
		return nil, nil, err
	}

	return &groupProcess{
		process:    cmd.Process,
		processPty: processPty,
	}, stdin, nil
}
Example #19
0
func runCommand(outCmd *exec.Cmd, request out.OutRequest) *Session {
	timeout := 10 * time.Second
	stdin, err := outCmd.StdinPipe()
	Ω(err).ShouldNot(HaveOccurred())

	session, err := Start(outCmd, GinkgoWriter, GinkgoWriter)
	Ω(err).ShouldNot(HaveOccurred())
	err = json.NewEncoder(stdin).Encode(request)
	Ω(err).ShouldNot(HaveOccurred())
	Eventually(session, timeout).Should(Exit(0))

	return session
}
Example #20
0
File: git.go Project: helgi/builder
// plumbCommand connects the exec in/output and the channel in/output.
//
// The sidechannel is for sending errors to logs.
func plumbCommand(cmd *exec.Cmd, channel ssh.Channel, sidechannel io.Writer) *sync.WaitGroup {
	var wg sync.WaitGroup
	inpipe, _ := cmd.StdinPipe()
	go func() {
		io.Copy(inpipe, channel)
		inpipe.Close()
	}()

	cmd.Stdout = channel
	cmd.Stderr = channel.Stderr()

	return &wg
}
Example #21
0
func assertWrite(t *testing.T, cmd *exec.Cmd, r io.Reader) string {
	wc, err := cmd.StdinPipe()
	if err != nil {
		t.Fatal(err)
	}
	io.Copy(wc, r)
	wc.Close()

	x, err := run(cmd)
	if err != nil {
		t.Fatal(err)
	}
	return x
}
Example #22
0
func runCommand(cmd *exec.Cmd, stdin []byte) (stdout []byte, err error) {
	pipeIn, _ := cmd.StdinPipe()

	pipeOut, _ := cmd.StdoutPipe()

	cmd.Start()
	pipeIn.Write(stdin)
	pipeIn.Close()

	stdout, _ = ioutil.ReadAll(pipeOut)
	pipeIn.Close()
	cmd.Wait()
	return stdout, nil
}
Example #23
0
File: archive.go Project: gpxl/deis
// CmdStream executes a command, and returns its stdout as a stream.
// If the command fails to run or doesn't complete successfully, an error
// will be returned, including anything written on stderr.
func CmdStream(cmd *exec.Cmd, input io.Reader) (io.ReadCloser, error) {
	if input != nil {
		stdin, err := cmd.StdinPipe()
		if err != nil {
			return nil, err
		}
		// Write stdin if any
		go func() {
			io.Copy(stdin, input)
			stdin.Close()
		}()
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}
	pipeR, pipeW := io.Pipe()
	errChan := make(chan []byte)
	// Collect stderr, we will use it in case of an error
	go func() {
		errText, e := ioutil.ReadAll(stderr)
		if e != nil {
			errText = []byte("(...couldn't fetch stderr: " + e.Error() + ")")
		}
		errChan <- errText
	}()
	// Copy stdout to the returned pipe
	go func() {
		_, err := io.Copy(pipeW, stdout)
		if err != nil {
			pipeW.CloseWithError(err)
		}
		errText := <-errChan
		if err := cmd.Wait(); err != nil {
			pipeW.CloseWithError(fmt.Errorf("%s: %s", err, errText))
		} else {
			pipeW.Close()
		}
	}()
	// Run the command and return the pipe
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	return pipeR, nil
}
func (sess *session) run(command *exec.Cmd) error {
	logger := sess.logger.Session("run")

	command.Stdout = sess.channel
	command.Stderr = sess.channel.Stderr()

	stdin, err := command.StdinPipe()
	if err != nil {
		return err
	}

	go helpers.CopyAndClose(logger.Session("to-stdin"), nil, stdin, sess.channel)

	return sess.runner.Start(command)
}
Example #25
0
func sendSawsInfo(config *Config, uuids string) error {
	jsonBytes := getJsonSawsInfo(config)
	jsonBytesReader := bytes.NewReader(jsonBytes)

	if config.InfoCmd != "" {
		name, cmdargs := getCmd(config.InfoCmd, uuids)
		cmd := exec.Cmd{Path: name, Args: cmdargs}
		writcl, err := cmd.StdinPipe()
		if err != nil {
			fmt.Println("Error running external infocmd: ", config.InfoCmd)
			panic(err)
		}
		cmd.Start()

		bytesn, err := io.Copy(writcl, jsonBytesReader)
		if err != nil {
			fmt.Println("Failed to read from jsonBytesReader and copy to writcl:", err)
		} else {
			fmt.Println(bytesn, "piped to", cmdargs)
		}
		err = writcl.Close()
		if err != nil {
			fmt.Println("Error failed to close write stream to stdin:", err)
		}

		err = cmd.Wait()
		if err != nil {
			fmt.Println("Subprocess returned error on wait:", err)
		}

	} else {

		key := "saws-info.json"
		uploader := s3manager.NewUploader(session.New())
		_, err := uploader.Upload(&s3manager.UploadInput{
			Bucket: &config.S3Bucket,
			Key:    &key,
			Body:   jsonBytesReader,
		})

		if err != nil {
			fmt.Println("Failed to upload saws-info.json.")
			return err
		}
	}

	return nil
}
Example #26
0
func NewCmdTest(cmd *exec.Cmd) *CmdTest {
	stdin, err := cmd.StdinPipe()
	if err != nil {
		panic(err)
	}
	pipeReader, pipeWriter := io.Pipe()
	cmd.Stdout = pipeWriter
	cmd.Stderr = pipeWriter

	cmdOutput := &CmdTest{
		reader: pipeReader,
		stdin:  stdin,
	}
	cmdOutput.output = cmdOutput.Run()
	return cmdOutput
}
func run(command *exec.Cmd, stdinContents []byte) *gexec.Session {
	fmt.Fprintf(GinkgoWriter, "input: %s\n", sanitize(string(stdinContents)))

	stdin, err := command.StdinPipe()
	Expect(err).ShouldNot(HaveOccurred())

	session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
	Expect(err).NotTo(HaveOccurred())

	_, err = io.WriteString(stdin, string(stdinContents))
	Expect(err).ShouldNot(HaveOccurred())

	err = stdin.Close()
	Expect(err).ShouldNot(HaveOccurred())

	return session
}
Example #28
0
func (s *StdConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error {
	command.Stdout = pipes.Stdout
	command.Stderr = pipes.Stderr

	if pipes.Stdin != nil {
		stdin, err := command.StdinPipe()
		if err != nil {
			return err
		}

		go func() {
			defer stdin.Close()
			io.Copy(stdin, pipes.Stdin)
		}()
	}
	return nil
}
Example #29
0
File: xpra.go Project: Safe3/oz
func writeFakeProfile(cmd *exec.Cmd) error {
	pi, err := cmd.StdinPipe()
	if err != nil {
		return nil
	}
	emptyProfile := new(oz.Profile)
	emptyProfile.Seccomp.Mode = "blacklist"
	emptyProfile.Seccomp.Enforce = true
	jdata, err := json.Marshal(emptyProfile)
	if err != nil {
		return err
	}
	io.Copy(pi, bytes.NewBuffer(jdata))
	pi.Close()

	return nil
}
Example #30
-1
// newConn executes the given command, returning an io.ReadWriteCloser attached to its stdout/stdin.
// The Cmd's Stdout/Stdin/Stderr are modified, but everything else (env, cwd, etc) is left intact.
// Takes ownership of cmd -- callers may not modify it after passing it to NewConn.
func newConn(cmd *exec.Cmd) (io.ReadWriteCloser, error) {
	cmd.Stderr = os.Stderr
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}
	conn := &conn{
		cmd:    cmd,
		stdout: stdout,
		stdin:  stdin,
		done:   make(chan bool),
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	go func() {
		conn.err = cmd.Wait()
		close(conn.done)
	}()
	return conn, nil
}