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