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