...

Source file src/os/exec/exec_test.go

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

View as plain text