Home | History | Annotate | Download | only in exec
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Use an external test to avoid os/exec -> net/http -> crypto/x509 -> os/exec
      6 // circular dependency on non-cgo darwin.
      7 
      8 package exec_test
      9 
     10 import (
     11 	"bufio"
     12 	"bytes"
     13 	"context"
     14 	"fmt"
     15 	"internal/poll"
     16 	"internal/testenv"
     17 	"io"
     18 	"io/ioutil"
     19 	"log"
     20 	"net"
     21 	"net/http"
     22 	"net/http/httptest"
     23 	"os"
     24 	"os/exec"
     25 	"path/filepath"
     26 	"runtime"
     27 	"strconv"
     28 	"strings"
     29 	"testing"
     30 	"time"
     31 )
     32 
     33 func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) {
     34 	testenv.MustHaveExec(t)
     35 
     36 	cs := []string{"-test.run=TestHelperProcess", "--"}
     37 	cs = append(cs, s...)
     38 	if ctx != nil {
     39 		cmd = exec.CommandContext(ctx, os.Args[0], cs...)
     40 	} else {
     41 		cmd = exec.Command(os.Args[0], cs...)
     42 	}
     43 	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
     44 	return cmd
     45 }
     46 
     47 func helperCommand(t *testing.T, s ...string) *exec.Cmd {
     48 	return helperCommandContext(t, nil, s...)
     49 }
     50 
     51 func TestEcho(t *testing.T) {
     52 	bs, err := helperCommand(t, "echo", "foo bar", "baz").Output()
     53 	if err != nil {
     54 		t.Errorf("echo: %v", err)
     55 	}
     56 	if g, e := string(bs), "foo bar baz\n"; g != e {
     57 		t.Errorf("echo: want %q, got %q", e, g)
     58 	}
     59 }
     60 
     61 func TestCommandRelativeName(t *testing.T) {
     62 	testenv.MustHaveExec(t)
     63 
     64 	// Run our own binary as a relative path
     65 	// (e.g. "_test/exec.test") our parent directory.
     66 	base := filepath.Base(os.Args[0]) // "exec.test"
     67 	dir := filepath.Dir(os.Args[0])   // "/tmp/go-buildNNNN/os/exec/_test"
     68 	if dir == "." {
     69 		t.Skip("skipping; running test at root somehow")
     70 	}
     71 	parentDir := filepath.Dir(dir) // "/tmp/go-buildNNNN/os/exec"
     72 	dirBase := filepath.Base(dir)  // "_test"
     73 	if dirBase == "." {
     74 		t.Skipf("skipping; unexpected shallow dir of %q", dir)
     75 	}
     76 
     77 	cmd := exec.Command(filepath.Join(dirBase, base), "-test.run=TestHelperProcess", "--", "echo", "foo")
     78 	cmd.Dir = parentDir
     79 	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
     80 
     81 	out, err := cmd.Output()
     82 	if err != nil {
     83 		t.Errorf("echo: %v", err)
     84 	}
     85 	if g, e := string(out), "foo\n"; g != e {
     86 		t.Errorf("echo: want %q, got %q", e, g)
     87 	}
     88 }
     89 
     90 func TestCatStdin(t *testing.T) {
     91 	// Cat, testing stdin and stdout.
     92 	input := "Input string\nLine 2"
     93 	p := helperCommand(t, "cat")
     94 	p.Stdin = strings.NewReader(input)
     95 	bs, err := p.Output()
     96 	if err != nil {
     97 		t.Errorf("cat: %v", err)
     98 	}
     99 	s := string(bs)
    100 	if s != input {
    101 		t.Errorf("cat: want %q, got %q", input, s)
    102 	}
    103 }
    104 
    105 func TestEchoFileRace(t *testing.T) {
    106 	cmd := helperCommand(t, "echo")
    107 	stdin, err := cmd.StdinPipe()
    108 	if err != nil {
    109 		t.Fatalf("StdinPipe: %v", err)
    110 	}
    111 	if err := cmd.Start(); err != nil {
    112 		t.Fatalf("Start: %v", err)
    113 	}
    114 	wrote := make(chan bool)
    115 	go func() {
    116 		defer close(wrote)
    117 		fmt.Fprint(stdin, "echo\n")
    118 	}()
    119 	if err := cmd.Wait(); err != nil {
    120 		t.Fatalf("Wait: %v", err)
    121 	}
    122 	<-wrote
    123 }
    124 
    125 func TestCatGoodAndBadFile(t *testing.T) {
    126 	// Testing combined output and error values.
    127 	bs, err := helperCommand(t, "cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
    128 	if _, ok := err.(*exec.ExitError); !ok {
    129 		t.Errorf("expected *exec.ExitError from cat combined; got %T: %v", err, err)
    130 	}
    131 	s := string(bs)
    132 	sp := strings.SplitN(s, "\n", 2)
    133 	if len(sp) != 2 {
    134 		t.Fatalf("expected two lines from cat; got %q", s)
    135 	}
    136 	errLine, body := sp[0], sp[1]
    137 	if !strings.HasPrefix(errLine, "Error: open /bogus/file.foo") {
    138 		t.Errorf("expected stderr to complain about file; got %q", errLine)
    139 	}
    140 	if !strings.Contains(body, "func TestHelperProcess(t *testing.T)") {
    141 		t.Errorf("expected test code; got %q (len %d)", body, len(body))
    142 	}
    143 }
    144 
    145 func TestNoExistBinary(t *testing.T) {
    146 	// Can't run a non-existent binary
    147 	err := exec.Command("/no-exist-binary").Run()
    148 	if err == nil {
    149 		t.Error("expected error from /no-exist-binary")
    150 	}
    151 }
    152 
    153 func TestExitStatus(t *testing.T) {
    154 	// Test that exit values are returned correctly
    155 	cmd := helperCommand(t, "exit", "42")
    156 	err := cmd.Run()
    157 	want := "exit status 42"
    158 	switch runtime.GOOS {
    159 	case "plan9":
    160 		want = fmt.Sprintf("exit status: '%s %d: 42'", filepath.Base(cmd.Path), cmd.ProcessState.Pid())
    161 	}
    162 	if werr, ok := err.(*exec.ExitError); ok {
    163 		if s := werr.Error(); s != want {
    164 			t.Errorf("from exit 42 got exit %q, want %q", s, want)
    165 		}
    166 	} else {
    167 		t.Fatalf("expected *exec.ExitError from exit 42; got %T: %v", err, err)
    168 	}
    169 }
    170 
    171 func TestPipes(t *testing.T) {
    172 	check := func(what string, err error) {
    173 		if err != nil {
    174 			t.Fatalf("%s: %v", what, err)
    175 		}
    176 	}
    177 	// Cat, testing stdin and stdout.
    178 	c := helperCommand(t, "pipetest")
    179 	stdin, err := c.StdinPipe()
    180 	check("StdinPipe", err)
    181 	stdout, err := c.StdoutPipe()
    182 	check("StdoutPipe", err)
    183 	stderr, err := c.StderrPipe()
    184 	check("StderrPipe", err)
    185 
    186 	outbr := bufio.NewReader(stdout)
    187 	errbr := bufio.NewReader(stderr)
    188 	line := func(what string, br *bufio.Reader) string {
    189 		line, _, err := br.ReadLine()
    190 		if err != nil {
    191 			t.Fatalf("%s: %v", what, err)
    192 		}
    193 		return string(line)
    194 	}
    195 
    196 	err = c.Start()
    197 	check("Start", err)
    198 
    199 	_, err = stdin.Write([]byte("O:I am output\n"))
    200 	check("first stdin Write", err)
    201 	if g, e := line("first output line", outbr), "O:I am output"; g != e {
    202 		t.Errorf("got %q, want %q", g, e)
    203 	}
    204 
    205 	_, err = stdin.Write([]byte("E:I am error\n"))
    206 	check("second stdin Write", err)
    207 	if g, e := line("first error line", errbr), "E:I am error"; g != e {
    208 		t.Errorf("got %q, want %q", g, e)
    209 	}
    210 
    211 	_, err = stdin.Write([]byte("O:I am output2\n"))
    212 	check("third stdin Write 3", err)
    213 	if g, e := line("second output line", outbr), "O:I am output2"; g != e {
    214 		t.Errorf("got %q, want %q", g, e)
    215 	}
    216 
    217 	stdin.Close()
    218 	err = c.Wait()
    219 	check("Wait", err)
    220 }
    221 
    222 const stdinCloseTestString = "Some test string."
    223 
    224 // Issue 6270.
    225 func TestStdinClose(t *testing.T) {
    226 	check := func(what string, err error) {
    227 		if err != nil {
    228 			t.Fatalf("%s: %v", what, err)
    229 		}
    230 	}
    231 	cmd := helperCommand(t, "stdinClose")
    232 	stdin, err := cmd.StdinPipe()
    233 	check("StdinPipe", err)
    234 	// Check that we can access methods of the underlying os.File.`
    235 	if _, ok := stdin.(interface {
    236 		Fd() uintptr
    237 	}); !ok {
    238 		t.Error("can't access methods of underlying *os.File")
    239 	}
    240 	check("Start", cmd.Start())
    241 	go func() {
    242 		_, err := io.Copy(stdin, strings.NewReader(stdinCloseTestString))
    243 		check("Copy", err)
    244 		// Before the fix, this next line would race with cmd.Wait.
    245 		check("Close", stdin.Close())
    246 	}()
    247 	check("Wait", cmd.Wait())
    248 }
    249 
    250 // Issue 17647.
    251 // It used to be the case that TestStdinClose, above, would fail when
    252 // run under the race detector. This test is a variant of TestStdinClose
    253 // that also used to fail when run under the race detector.
    254 // This test is run by cmd/dist under the race detector to verify that
    255 // the race detector no longer reports any problems.
    256 func TestStdinCloseRace(t *testing.T) {
    257 	cmd := helperCommand(t, "stdinClose")
    258 	stdin, err := cmd.StdinPipe()
    259 	if err != nil {
    260 		t.Fatalf("StdinPipe: %v", err)
    261 	}
    262 	if err := cmd.Start(); err != nil {
    263 		t.Fatalf("Start: %v", err)
    264 	}
    265 	go func() {
    266 		// We don't check the error return of Kill. It is
    267 		// possible that the process has already exited, in
    268 		// which case Kill will return an error "process
    269 		// already finished". The purpose of this test is to
    270 		// see whether the race detector reports an error; it
    271 		// doesn't matter whether this Kill succeeds or not.
    272 		cmd.Process.Kill()
    273 	}()
    274 	go func() {
    275 		// Send the wrong string, so that the child fails even
    276 		// if the other goroutine doesn't manage to kill it first.
    277 		// This test is to check that the race detector does not
    278 		// falsely report an error, so it doesn't matter how the
    279 		// child process fails.
    280 		io.Copy(stdin, strings.NewReader("unexpected string"))
    281 		if err := stdin.Close(); err != nil {
    282 			t.Errorf("stdin.Close: %v", err)
    283 		}
    284 	}()
    285 	if err := cmd.Wait(); err == nil {
    286 		t.Fatalf("Wait: succeeded unexpectedly")
    287 	}
    288 }
    289 
    290 // Issue 5071
    291 func TestPipeLookPathLeak(t *testing.T) {
    292 	// If we are reading from /proc/self/fd we (should) get an exact result.
    293 	tolerance := 0
    294 
    295 	// Reading /proc/self/fd is more reliable than calling lsof, so try that
    296 	// first.
    297 	numOpenFDs := func() (int, []byte, error) {
    298 		fds, err := ioutil.ReadDir("/proc/self/fd")
    299 		if err != nil {
    300 			return 0, nil, err
    301 		}
    302 		return len(fds), nil, nil
    303 	}
    304 	want, before, err := numOpenFDs()
    305 	if err != nil {
    306 		// We encountered a problem reading /proc/self/fd (we might be on
    307 		// a platform that doesn't have it). Fall back onto lsof.
    308 		t.Logf("using lsof because: %v", err)
    309 		numOpenFDs = func() (int, []byte, error) {
    310 			// Android's stock lsof does not obey the -p option,
    311 			// so extra filtering is needed.
    312 			// https://golang.org/issue/10206
    313 			if runtime.GOOS == "android" {
    314 				// numOpenFDsAndroid handles errors itself and
    315 				// might skip or fail the test.
    316 				n, lsof := numOpenFDsAndroid(t)
    317 				return n, lsof, nil
    318 			}
    319 			lsof, err := exec.Command("lsof", "-b", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
    320 			return bytes.Count(lsof, []byte("\n")), lsof, err
    321 		}
    322 
    323 		// lsof may see file descriptors associated with the fork itself,
    324 		// so we allow some extra margin if we have to use it.
    325 		// https://golang.org/issue/19243
    326 		tolerance = 5
    327 
    328 		// Retry reading the number of open file descriptors.
    329 		want, before, err = numOpenFDs()
    330 		if err != nil {
    331 			t.Log(err)
    332 			t.Skipf("skipping test; error finding or running lsof")
    333 		}
    334 	}
    335 
    336 	for i := 0; i < 6; i++ {
    337 		cmd := exec.Command("something-that-does-not-exist-binary")
    338 		cmd.StdoutPipe()
    339 		cmd.StderrPipe()
    340 		cmd.StdinPipe()
    341 		if err := cmd.Run(); err == nil {
    342 			t.Fatal("unexpected success")
    343 		}
    344 	}
    345 	got, after, err := numOpenFDs()
    346 	if err != nil {
    347 		// numOpenFDs has already succeeded once, it should work here.
    348 		t.Errorf("unexpected failure: %v", err)
    349 	}
    350 	if got-want > tolerance {
    351 		t.Errorf("number of open file descriptors changed: got %v, want %v", got, want)
    352 		if before != nil {
    353 			t.Errorf("before:\n%v\n", before)
    354 		}
    355 		if after != nil {
    356 			t.Errorf("after:\n%v\n", after)
    357 		}
    358 	}
    359 }
    360 
    361 func numOpenFDsAndroid(t *testing.T) (n int, lsof []byte) {
    362 	raw, err := exec.Command("lsof").Output()
    363 	if err != nil {
    364 		t.Skip("skipping test; error finding or running lsof")
    365 	}
    366 
    367 	// First find the PID column index by parsing the first line, and
    368 	// select lines containing pid in the column.
    369 	pid := []byte(strconv.Itoa(os.Getpid()))
    370 	pidCol := -1
    371 
    372 	s := bufio.NewScanner(bytes.NewReader(raw))
    373 	for s.Scan() {
    374 		line := s.Bytes()
    375 		fields := bytes.Fields(line)
    376 		if pidCol < 0 {
    377 			for i, v := range fields {
    378 				if bytes.Equal(v, []byte("PID")) {
    379 					pidCol = i
    380 					break
    381 				}
    382 			}
    383 			lsof = append(lsof, line...)
    384 			continue
    385 		}
    386 		if bytes.Equal(fields[pidCol], pid) {
    387 			lsof = append(lsof, '\n')
    388 			lsof = append(lsof, line...)
    389 		}
    390 	}
    391 	if pidCol < 0 {
    392 		t.Fatal("error processing lsof output: unexpected header format")
    393 	}
    394 	if err := s.Err(); err != nil {
    395 		t.Fatalf("error processing lsof output: %v", err)
    396 	}
    397 	return bytes.Count(lsof, []byte("\n")), lsof
    398 }
    399 
    400 var testedAlreadyLeaked = false
    401 
    402 // basefds returns the number of expected file descriptors
    403 // to be present in a process at start.
    404 // stdin, stdout, stderr, epoll/kqueue, maybe testlog
    405 func basefds() uintptr {
    406 	n := os.Stderr.Fd() + 1
    407 	for _, arg := range os.Args {
    408 		if strings.HasPrefix(arg, "-test.testlogfile=") {
    409 			n++
    410 		}
    411 	}
    412 	return n
    413 }
    414 
    415 func closeUnexpectedFds(t *testing.T, m string) {
    416 	for fd := basefds(); fd <= 101; fd++ {
    417 		if fd == poll.PollDescriptor() {
    418 			continue
    419 		}
    420 		err := os.NewFile(fd, "").Close()
    421 		if err == nil {
    422 			t.Logf("%s: Something already leaked - closed fd %d", m, fd)
    423 		}
    424 	}
    425 }
    426 
    427 func TestExtraFilesFDShuffle(t *testing.T) {
    428 	t.Skip("flaky test; see https://golang.org/issue/5780")
    429 	switch runtime.GOOS {
    430 	case "darwin":
    431 		// TODO(cnicolaou): https://golang.org/issue/2603
    432 		// leads to leaked file descriptors in this test when it's
    433 		// run from a builder.
    434 		closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
    435 	case "netbsd":
    436 		// https://golang.org/issue/3955
    437 		closeUnexpectedFds(t, "TestExtraFilesFDShuffle")
    438 	case "windows":
    439 		t.Skip("no operating system support; skipping")
    440 	}
    441 
    442 	// syscall.StartProcess maps all the FDs passed to it in
    443 	// ProcAttr.Files (the concatenation of stdin,stdout,stderr and
    444 	// ExtraFiles) into consecutive FDs in the child, that is:
    445 	// Files{11, 12, 6, 7, 9, 3} should result in the file
    446 	// represented by FD 11 in the parent being made available as 0
    447 	// in the child, 12 as 1, etc.
    448 	//
    449 	// We want to test that FDs in the child do not get overwritten
    450 	// by one another as this shuffle occurs. The original implementation
    451 	// was buggy in that in some data dependent cases it would overwrite
    452 	// stderr in the child with one of the ExtraFile members.
    453 	// Testing for this case is difficult because it relies on using
    454 	// the same FD values as that case. In particular, an FD of 3
    455 	// must be at an index of 4 or higher in ProcAttr.Files and
    456 	// the FD of the write end of the Stderr pipe (as obtained by
    457 	// StderrPipe()) must be the same as the size of ProcAttr.Files;
    458 	// therefore we test that the read end of this pipe (which is what
    459 	// is returned to the parent by StderrPipe() being one less than
    460 	// the size of ProcAttr.Files, i.e. 3+len(cmd.ExtraFiles).
    461 	//
    462 	// Moving this test case around within the overall tests may
    463 	// affect the FDs obtained and hence the checks to catch these cases.
    464 	npipes := 2
    465 	c := helperCommand(t, "extraFilesAndPipes", strconv.Itoa(npipes+1))
    466 	rd, wr, _ := os.Pipe()
    467 	defer rd.Close()
    468 	if rd.Fd() != 3 {
    469 		t.Errorf("bad test value for test pipe: fd %d", rd.Fd())
    470 	}
    471 	stderr, _ := c.StderrPipe()
    472 	wr.WriteString("_LAST")
    473 	wr.Close()
    474 
    475 	pipes := make([]struct {
    476 		r, w *os.File
    477 	}, npipes)
    478 	data := []string{"a", "b"}
    479 
    480 	for i := 0; i < npipes; i++ {
    481 		r, w, err := os.Pipe()
    482 		if err != nil {
    483 			t.Fatalf("unexpected error creating pipe: %s", err)
    484 		}
    485 		pipes[i].r = r
    486 		pipes[i].w = w
    487 		w.WriteString(data[i])
    488 		c.ExtraFiles = append(c.ExtraFiles, pipes[i].r)
    489 		defer func() {
    490 			r.Close()
    491 			w.Close()
    492 		}()
    493 	}
    494 	// Put fd 3 at the end.
    495 	c.ExtraFiles = append(c.ExtraFiles, rd)
    496 
    497 	stderrFd := int(stderr.(*os.File).Fd())
    498 	if stderrFd != ((len(c.ExtraFiles) + 3) - 1) {
    499 		t.Errorf("bad test value for stderr pipe")
    500 	}
    501 
    502 	expected := "child: " + strings.Join(data, "") + "_LAST"
    503 
    504 	err := c.Start()
    505 	if err != nil {
    506 		t.Fatalf("Run: %v", err)
    507 	}
    508 	ch := make(chan string, 1)
    509 	go func(ch chan string) {
    510 		buf := make([]byte, 512)
    511 		n, err := stderr.Read(buf)
    512 		if err != nil {
    513 			t.Errorf("Read: %s", err)
    514 			ch <- err.Error()
    515 		} else {
    516 			ch <- string(buf[:n])
    517 		}
    518 		close(ch)
    519 	}(ch)
    520 	select {
    521 	case m := <-ch:
    522 		if m != expected {
    523 			t.Errorf("Read: '%s' not '%s'", m, expected)
    524 		}
    525 	case <-time.After(5 * time.Second):
    526 		t.Errorf("Read timedout")
    527 	}
    528 	c.Wait()
    529 }
    530 
    531 func TestExtraFiles(t *testing.T) {
    532 	testenv.MustHaveExec(t)
    533 
    534 	if runtime.GOOS == "windows" {
    535 		t.Skipf("skipping test on %q", runtime.GOOS)
    536 	}
    537 
    538 	// Ensure that file descriptors have not already been leaked into
    539 	// our environment.
    540 	if !testedAlreadyLeaked {
    541 		testedAlreadyLeaked = true
    542 		closeUnexpectedFds(t, "TestExtraFiles")
    543 	}
    544 
    545 	// Force network usage, to verify the epoll (or whatever) fd
    546 	// doesn't leak to the child,
    547 	ln, err := net.Listen("tcp", "127.0.0.1:0")
    548 	if err != nil {
    549 		t.Fatal(err)
    550 	}
    551 	defer ln.Close()
    552 
    553 	// Make sure duplicated fds don't leak to the child.
    554 	f, err := ln.(*net.TCPListener).File()
    555 	if err != nil {
    556 		t.Fatal(err)
    557 	}
    558 	defer f.Close()
    559 	ln2, err := net.FileListener(f)
    560 	if err != nil {
    561 		t.Fatal(err)
    562 	}
    563 	defer ln2.Close()
    564 
    565 	// Force TLS root certs to be loaded (which might involve
    566 	// cgo), to make sure none of that potential C code leaks fds.
    567 	ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
    568 	// quiet expected TLS handshake error "remote error: bad certificate"
    569 	ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
    570 	ts.StartTLS()
    571 	defer ts.Close()
    572 	_, err = http.Get(ts.URL)
    573 	if err == nil {
    574 		t.Errorf("success trying to fetch %s; want an error", ts.URL)
    575 	}
    576 
    577 	tf, err := ioutil.TempFile("", "")
    578 	if err != nil {
    579 		t.Fatalf("TempFile: %v", err)
    580 	}
    581 	defer os.Remove(tf.Name())
    582 	defer tf.Close()
    583 
    584 	const text = "Hello, fd 3!"
    585 	_, err = tf.Write([]byte(text))
    586 	if err != nil {
    587 		t.Fatalf("Write: %v", err)
    588 	}
    589 	_, err = tf.Seek(0, io.SeekStart)
    590 	if err != nil {
    591 		t.Fatalf("Seek: %v", err)
    592 	}
    593 
    594 	c := helperCommand(t, "read3")
    595 	var stdout, stderr bytes.Buffer
    596 	c.Stdout = &stdout
    597 	c.Stderr = &stderr
    598 	c.ExtraFiles = []*os.File{tf}
    599 	err = c.Run()
    600 	if err != nil {
    601 		t.Fatalf("Run: %v; stdout %q, stderr %q", err, stdout.Bytes(), stderr.Bytes())
    602 	}
    603 	if stdout.String() != text {
    604 		t.Errorf("got stdout %q, stderr %q; want %q on stdout", stdout.String(), stderr.String(), text)
    605 	}
    606 }
    607 
    608 func TestExtraFilesRace(t *testing.T) {
    609 	if runtime.GOOS == "windows" {
    610 		t.Skip("no operating system support; skipping")
    611 	}
    612 	listen := func() net.Listener {
    613 		ln, err := net.Listen("tcp", "127.0.0.1:0")
    614 		if err != nil {
    615 			t.Fatal(err)
    616 		}
    617 		return ln
    618 	}
    619 	listenerFile := func(ln net.Listener) *os.File {
    620 		f, err := ln.(*net.TCPListener).File()
    621 		if err != nil {
    622 			t.Fatal(err)
    623 		}
    624 		return f
    625 	}
    626 	runCommand := func(c *exec.Cmd, out chan<- string) {
    627 		bout, err := c.CombinedOutput()
    628 		if err != nil {
    629 			out <- "ERROR:" + err.Error()
    630 		} else {
    631 			out <- string(bout)
    632 		}
    633 	}
    634 
    635 	for i := 0; i < 10; i++ {
    636 		la := listen()
    637 		ca := helperCommand(t, "describefiles")
    638 		ca.ExtraFiles = []*os.File{listenerFile(la)}
    639 		lb := listen()
    640 		cb := helperCommand(t, "describefiles")
    641 		cb.ExtraFiles = []*os.File{listenerFile(lb)}
    642 		ares := make(chan string)
    643 		bres := make(chan string)
    644 		go runCommand(ca, ares)
    645 		go runCommand(cb, bres)
    646 		if got, want := <-ares, fmt.Sprintf("fd3: listener %s\n", la.Addr()); got != want {
    647 			t.Errorf("iteration %d, process A got:\n%s\nwant:\n%s\n", i, got, want)
    648 		}
    649 		if got, want := <-bres, fmt.Sprintf("fd3: listener %s\n", lb.Addr()); got != want {
    650 			t.Errorf("iteration %d, process B got:\n%s\nwant:\n%s\n", i, got, want)
    651 		}
    652 		la.Close()
    653 		lb.Close()
    654 		for _, f := range ca.ExtraFiles {
    655 			f.Close()
    656 		}
    657 		for _, f := range cb.ExtraFiles {
    658 			f.Close()
    659 		}
    660 
    661 	}
    662 }
    663 
    664 // TestHelperProcess isn't a real test. It's used as a helper process
    665 // for TestParameterRun.
    666 func TestHelperProcess(*testing.T) {
    667 	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
    668 		return
    669 	}
    670 	defer os.Exit(0)
    671 
    672 	// Determine which command to use to display open files.
    673 	ofcmd := "lsof"
    674 	switch runtime.GOOS {
    675 	case "dragonfly", "freebsd", "netbsd", "openbsd":
    676 		ofcmd = "fstat"
    677 	case "plan9":
    678 		ofcmd = "/bin/cat"
    679 	}
    680 
    681 	args := os.Args
    682 	for len(args) > 0 {
    683 		if args[0] == "--" {
    684 			args = args[1:]
    685 			break
    686 		}
    687 		args = args[1:]
    688 	}
    689 	if len(args) == 0 {
    690 		fmt.Fprintf(os.Stderr, "No command\n")
    691 		os.Exit(2)
    692 	}
    693 
    694 	cmd, args := args[0], args[1:]
    695 	switch cmd {
    696 	case "echo":
    697 		iargs := []interface{}{}
    698 		for _, s := range args {
    699 			iargs = append(iargs, s)
    700 		}
    701 		fmt.Println(iargs...)
    702 	case "echoenv":
    703 		for _, s := range args {
    704 			fmt.Println(os.Getenv(s))
    705 		}
    706 		os.Exit(0)
    707 	case "cat":
    708 		if len(args) == 0 {
    709 			io.Copy(os.Stdout, os.Stdin)
    710 			return
    711 		}
    712 		exit := 0
    713 		for _, fn := range args {
    714 			f, err := os.Open(fn)
    715 			if err != nil {
    716 				fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    717 				exit = 2
    718 			} else {
    719 				defer f.Close()
    720 				io.Copy(os.Stdout, f)
    721 			}
    722 		}
    723 		os.Exit(exit)
    724 	case "pipetest":
    725 		bufr := bufio.NewReader(os.Stdin)
    726 		for {
    727 			line, _, err := bufr.ReadLine()
    728 			if err == io.EOF {
    729 				break
    730 			} else if err != nil {
    731 				os.Exit(1)
    732 			}
    733 			if bytes.HasPrefix(line, []byte("O:")) {
    734 				os.Stdout.Write(line)
    735 				os.Stdout.Write([]byte{'\n'})
    736 			} else if bytes.HasPrefix(line, []byte("E:")) {
    737 				os.Stderr.Write(line)
    738 				os.Stderr.Write([]byte{'\n'})
    739 			} else {
    740 				os.Exit(1)
    741 			}
    742 		}
    743 	case "stdinClose":
    744 		b, err := ioutil.ReadAll(os.Stdin)
    745 		if err != nil {
    746 			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    747 			os.Exit(1)
    748 		}
    749 		if s := string(b); s != stdinCloseTestString {
    750 			fmt.Fprintf(os.Stderr, "Error: Read %q, want %q", s, stdinCloseTestString)
    751 			os.Exit(1)
    752 		}
    753 		os.Exit(0)
    754 	case "read3": // read fd 3
    755 		fd3 := os.NewFile(3, "fd3")
    756 		bs, err := ioutil.ReadAll(fd3)
    757 		if err != nil {
    758 			fmt.Printf("ReadAll from fd 3: %v", err)
    759 			os.Exit(1)
    760 		}
    761 		switch runtime.GOOS {
    762 		case "dragonfly":
    763 			// TODO(jsing): Determine why DragonFly is leaking
    764 			// file descriptors...
    765 		case "darwin":
    766 			// TODO(bradfitz): broken? Sometimes.
    767 			// https://golang.org/issue/2603
    768 			// Skip this additional part of the test for now.
    769 		case "netbsd":
    770 			// TODO(jsing): This currently fails on NetBSD due to
    771 			// the cloned file descriptors that result from opening
    772 			// /dev/urandom.
    773 			// https://golang.org/issue/3955
    774 		case "solaris":
    775 			// TODO(aram): This fails on Solaris because libc opens
    776 			// its own files, as it sees fit. Darwin does the same,
    777 			// see: https://golang.org/issue/2603
    778 		default:
    779 			// Now verify that there are no other open fds.
    780 			var files []*os.File
    781 			for wantfd := basefds() + 1; wantfd <= 100; wantfd++ {
    782 				if wantfd == poll.PollDescriptor() {
    783 					continue
    784 				}
    785 				f, err := os.Open(os.Args[0])
    786 				if err != nil {
    787 					fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
    788 					os.Exit(1)
    789 				}
    790 				if got := f.Fd(); got != wantfd {
    791 					fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
    792 					var args []string
    793 					switch runtime.GOOS {
    794 					case "plan9":
    795 						args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
    796 					default:
    797 						args = []string{"-p", fmt.Sprint(os.Getpid())}
    798 					}
    799 					out, _ := exec.Command(ofcmd, args...).CombinedOutput()
    800 					fmt.Print(string(out))
    801 					os.Exit(1)
    802 				}
    803 				files = append(files, f)
    804 			}
    805 			for _, f := range files {
    806 				f.Close()
    807 			}
    808 		}
    809 		// Referring to fd3 here ensures that it is not
    810 		// garbage collected, and therefore closed, while
    811 		// executing the wantfd loop above. It doesn't matter
    812 		// what we do with fd3 as long as we refer to it;
    813 		// closing it is the easy choice.
    814 		fd3.Close()
    815 		os.Stdout.Write(bs)
    816 	case "exit":
    817 		n, _ := strconv.Atoi(args[0])
    818 		os.Exit(n)
    819 	case "describefiles":
    820 		f := os.NewFile(3, fmt.Sprintf("fd3"))
    821 		ln, err := net.FileListener(f)
    822 		if err == nil {
    823 			fmt.Printf("fd3: listener %s\n", ln.Addr())
    824 			ln.Close()
    825 		}
    826 		os.Exit(0)
    827 	case "extraFilesAndPipes":
    828 		n, _ := strconv.Atoi(args[0])
    829 		pipes := make([]*os.File, n)
    830 		for i := 0; i < n; i++ {
    831 			pipes[i] = os.NewFile(uintptr(3+i), strconv.Itoa(i))
    832 		}
    833 		response := ""
    834 		for i, r := range pipes {
    835 			ch := make(chan string, 1)
    836 			go func(c chan string) {
    837 				buf := make([]byte, 10)
    838 				n, err := r.Read(buf)
    839 				if err != nil {
    840 					fmt.Fprintf(os.Stderr, "Child: read error: %v on pipe %d\n", err, i)
    841 					os.Exit(1)
    842 				}
    843 				c <- string(buf[:n])
    844 				close(c)
    845 			}(ch)
    846 			select {
    847 			case m := <-ch:
    848 				response = response + m
    849 			case <-time.After(5 * time.Second):
    850 				fmt.Fprintf(os.Stderr, "Child: Timeout reading from pipe: %d\n", i)
    851 				os.Exit(1)
    852 			}
    853 		}
    854 		fmt.Fprintf(os.Stderr, "child: %s", response)
    855 		os.Exit(0)
    856 	case "exec":
    857 		cmd := exec.Command(args[1])
    858 		cmd.Dir = args[0]
    859 		output, err := cmd.CombinedOutput()
    860 		if err != nil {
    861 			fmt.Fprintf(os.Stderr, "Child: %s %s", err, string(output))
    862 			os.Exit(1)
    863 		}
    864 		fmt.Printf("%s", string(output))
    865 		os.Exit(0)
    866 	case "lookpath":
    867 		p, err := exec.LookPath(args[0])
    868 		if err != nil {
    869 			fmt.Fprintf(os.Stderr, "LookPath failed: %v\n", err)
    870 			os.Exit(1)
    871 		}
    872 		fmt.Print(p)
    873 		os.Exit(0)
    874 	case "stderrfail":
    875 		fmt.Fprintf(os.Stderr, "some stderr text\n")
    876 		os.Exit(1)
    877 	case "sleep":
    878 		time.Sleep(3 * time.Second)
    879 		os.Exit(0)
    880 	default:
    881 		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
    882 		os.Exit(2)
    883 	}
    884 }
    885 
    886 type delayedInfiniteReader struct{}
    887 
    888 func (delayedInfiniteReader) Read(b []byte) (int, error) {
    889 	time.Sleep(100 * time.Millisecond)
    890 	for i := range b {
    891 		b[i] = 'x'
    892 	}
    893 	return len(b), nil
    894 }
    895 
    896 // Issue 9173: ignore stdin pipe writes if the program completes successfully.
    897 func TestIgnorePipeErrorOnSuccess(t *testing.T) {
    898 	testenv.MustHaveExec(t)
    899 
    900 	// We really only care about testing this on Unixy and Windowsy things.
    901 	if runtime.GOOS == "plan9" {
    902 		t.Skipf("skipping test on %q", runtime.GOOS)
    903 	}
    904 
    905 	testWith := func(r io.Reader) func(*testing.T) {
    906 		return func(t *testing.T) {
    907 			cmd := helperCommand(t, "echo", "foo")
    908 			var out bytes.Buffer
    909 			cmd.Stdin = r
    910 			cmd.Stdout = &out
    911 			if err := cmd.Run(); err != nil {
    912 				t.Fatal(err)
    913 			}
    914 			if got, want := out.String(), "foo\n"; got != want {
    915 				t.Errorf("output = %q; want %q", got, want)
    916 			}
    917 		}
    918 	}
    919 	t.Run("10MB", testWith(strings.NewReader(strings.Repeat("x", 10<<20))))
    920 	t.Run("Infinite", testWith(delayedInfiniteReader{}))
    921 }
    922 
    923 type badWriter struct{}
    924 
    925 func (w *badWriter) Write(data []byte) (int, error) {
    926 	return 0, io.ErrUnexpectedEOF
    927 }
    928 
    929 func TestClosePipeOnCopyError(t *testing.T) {
    930 	testenv.MustHaveExec(t)
    931 
    932 	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
    933 		t.Skipf("skipping test on %s - no yes command", runtime.GOOS)
    934 	}
    935 	cmd := exec.Command("yes")
    936 	cmd.Stdout = new(badWriter)
    937 	c := make(chan int, 1)
    938 	go func() {
    939 		err := cmd.Run()
    940 		if err == nil {
    941 			t.Errorf("yes completed successfully")
    942 		}
    943 		c <- 1
    944 	}()
    945 	select {
    946 	case <-c:
    947 		// ok
    948 	case <-time.After(5 * time.Second):
    949 		t.Fatalf("yes got stuck writing to bad writer")
    950 	}
    951 }
    952 
    953 func TestOutputStderrCapture(t *testing.T) {
    954 	testenv.MustHaveExec(t)
    955 
    956 	cmd := helperCommand(t, "stderrfail")
    957 	_, err := cmd.Output()
    958 	ee, ok := err.(*exec.ExitError)
    959 	if !ok {
    960 		t.Fatalf("Output error type = %T; want ExitError", err)
    961 	}
    962 	got := string(ee.Stderr)
    963 	want := "some stderr text\n"
    964 	if got != want {
    965 		t.Errorf("ExitError.Stderr = %q; want %q", got, want)
    966 	}
    967 }
    968 
    969 func TestContext(t *testing.T) {
    970 	ctx, cancel := context.WithCancel(context.Background())
    971 	c := helperCommandContext(t, ctx, "pipetest")
    972 	stdin, err := c.StdinPipe()
    973 	if err != nil {
    974 		t.Fatal(err)
    975 	}
    976 	stdout, err := c.StdoutPipe()
    977 	if err != nil {
    978 		t.Fatal(err)
    979 	}
    980 	if err := c.Start(); err != nil {
    981 		t.Fatal(err)
    982 	}
    983 
    984 	if _, err := stdin.Write([]byte("O:hi\n")); err != nil {
    985 		t.Fatal(err)
    986 	}
    987 	buf := make([]byte, 5)
    988 	n, err := io.ReadFull(stdout, buf)
    989 	if n != len(buf) || err != nil || string(buf) != "O:hi\n" {
    990 		t.Fatalf("ReadFull = %d, %v, %q", n, err, buf[:n])
    991 	}
    992 	waitErr := make(chan error, 1)
    993 	go func() {
    994 		waitErr <- c.Wait()
    995 	}()
    996 	cancel()
    997 	select {
    998 	case err := <-waitErr:
    999 		if err == nil {
   1000 			t.Fatal("expected Wait failure")
   1001 		}
   1002 	case <-time.After(3 * time.Second):
   1003 		t.Fatal("timeout waiting for child process death")
   1004 	}
   1005 }
   1006 
   1007 func TestContextCancel(t *testing.T) {
   1008 	if testenv.Builder() == "windows-386-xp" {
   1009 		t.Skipf("known to fail on Windows XP. Issue 17245")
   1010 	}
   1011 	ctx, cancel := context.WithCancel(context.Background())
   1012 	defer cancel()
   1013 	c := helperCommandContext(t, ctx, "cat")
   1014 
   1015 	r, w, err := os.Pipe()
   1016 	if err != nil {
   1017 		t.Fatal(err)
   1018 	}
   1019 	c.Stdin = r
   1020 
   1021 	stdout, err := c.StdoutPipe()
   1022 	if err != nil {
   1023 		t.Fatal(err)
   1024 	}
   1025 	readDone := make(chan struct{})
   1026 	go func() {
   1027 		defer close(readDone)
   1028 		var a [1024]byte
   1029 		for {
   1030 			n, err := stdout.Read(a[:])
   1031 			if err != nil {
   1032 				if err != io.EOF {
   1033 					t.Errorf("unexpected read error: %v", err)
   1034 				}
   1035 				return
   1036 			}
   1037 			t.Logf("%s", a[:n])
   1038 		}
   1039 	}()
   1040 
   1041 	if err := c.Start(); err != nil {
   1042 		t.Fatal(err)
   1043 	}
   1044 
   1045 	if err := r.Close(); err != nil {
   1046 		t.Fatal(err)
   1047 	}
   1048 
   1049 	if _, err := io.WriteString(w, "echo"); err != nil {
   1050 		t.Fatal(err)
   1051 	}
   1052 
   1053 	cancel()
   1054 
   1055 	// Calling cancel should have killed the process, so writes
   1056 	// should now fail.  Give the process a little while to die.
   1057 	start := time.Now()
   1058 	for {
   1059 		if _, err := io.WriteString(w, "echo"); err != nil {
   1060 			break
   1061 		}
   1062 		if time.Since(start) > time.Second {
   1063 			t.Fatal("canceling context did not stop program")
   1064 		}
   1065 		time.Sleep(time.Millisecond)
   1066 	}
   1067 
   1068 	if err := w.Close(); err != nil {
   1069 		t.Errorf("error closing write end of pipe: %v", err)
   1070 	}
   1071 	<-readDone
   1072 
   1073 	if err := c.Wait(); err == nil {
   1074 		t.Error("program unexpectedly exited successfully")
   1075 	} else {
   1076 		t.Logf("exit status: %v", err)
   1077 	}
   1078 }
   1079 
   1080 // test that environment variables are de-duped.
   1081 func TestDedupEnvEcho(t *testing.T) {
   1082 	testenv.MustHaveExec(t)
   1083 
   1084 	cmd := helperCommand(t, "echoenv", "FOO")
   1085 	cmd.Env = append(cmd.Env, "FOO=bad", "FOO=good")
   1086 	out, err := cmd.CombinedOutput()
   1087 	if err != nil {
   1088 		t.Fatal(err)
   1089 	}
   1090 	if got, want := strings.TrimSpace(string(out)), "good"; got != want {
   1091 		t.Errorf("output = %q; want %q", got, want)
   1092 	}
   1093 }
   1094