github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/os/exec/exec_test.go (about)

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