github.com/rafaeltorres324/go/src@v0.0.0-20210519164414-9fdf653a9838/os/exec/read3.go (about)

     1  // Copyright 2020 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  // +build ignore
     6  
     7  // This is a test program that verifies that it can read from
     8  // descriptor 3 and that no other descriptors are open.
     9  // This is not done via TestHelperProcess and GO_WANT_HELPER_PROCESS
    10  // because we want to ensure that this program does not use cgo,
    11  // because C libraries can open file descriptors behind our backs
    12  // and confuse the test. See issue 25628.
    13  package main
    14  
    15  import (
    16  	"fmt"
    17  	"internal/poll"
    18  	"io"
    19  	"os"
    20  	"os/exec"
    21  	"runtime"
    22  	"strings"
    23  )
    24  
    25  func main() {
    26  	fd3 := os.NewFile(3, "fd3")
    27  	bs, err := io.ReadAll(fd3)
    28  	if err != nil {
    29  		fmt.Printf("ReadAll from fd 3: %v\n", err)
    30  		os.Exit(1)
    31  	}
    32  
    33  	// Now verify that there are no other open fds.
    34  	// stdin == 0
    35  	// stdout == 1
    36  	// stderr == 2
    37  	// descriptor from parent == 3
    38  	// All descriptors 4 and up should be available,
    39  	// except for any used by the network poller.
    40  	var files []*os.File
    41  	for wantfd := uintptr(4); wantfd <= 100; wantfd++ {
    42  		if poll.IsPollDescriptor(wantfd) {
    43  			continue
    44  		}
    45  		f, err := os.Open(os.Args[0])
    46  		if err != nil {
    47  			fmt.Printf("error opening file with expected fd %d: %v", wantfd, err)
    48  			os.Exit(1)
    49  		}
    50  		if got := f.Fd(); got != wantfd {
    51  			fmt.Printf("leaked parent file. fd = %d; want %d\n", got, wantfd)
    52  			fdfile := fmt.Sprintf("/proc/self/fd/%d", wantfd)
    53  			link, err := os.Readlink(fdfile)
    54  			fmt.Printf("readlink(%q) = %q, %v\n", fdfile, link, err)
    55  			var args []string
    56  			switch runtime.GOOS {
    57  			case "plan9":
    58  				args = []string{fmt.Sprintf("/proc/%d/fd", os.Getpid())}
    59  			case "aix", "solaris", "illumos":
    60  				args = []string{fmt.Sprint(os.Getpid())}
    61  			default:
    62  				args = []string{"-p", fmt.Sprint(os.Getpid())}
    63  			}
    64  
    65  			// Determine which command to use to display open files.
    66  			ofcmd := "lsof"
    67  			switch runtime.GOOS {
    68  			case "dragonfly", "freebsd", "netbsd", "openbsd":
    69  				ofcmd = "fstat"
    70  			case "plan9":
    71  				ofcmd = "/bin/cat"
    72  			case "aix":
    73  				ofcmd = "procfiles"
    74  			case "solaris", "illumos":
    75  				ofcmd = "pfiles"
    76  			}
    77  
    78  			cmd := exec.Command(ofcmd, args...)
    79  			out, err := cmd.CombinedOutput()
    80  			if err != nil {
    81  				fmt.Fprintf(os.Stderr, "%s failed: %v\n", strings.Join(cmd.Args, " "), err)
    82  			}
    83  			fmt.Printf("%s", out)
    84  			os.Exit(1)
    85  		}
    86  		files = append(files, f)
    87  	}
    88  
    89  	for _, f := range files {
    90  		f.Close()
    91  	}
    92  
    93  	// Referring to fd3 here ensures that it is not
    94  	// garbage collected, and therefore closed, while
    95  	// executing the wantfd loop above. It doesn't matter
    96  	// what we do with fd3 as long as we refer to it;
    97  	// closing it is the easy choice.
    98  	fd3.Close()
    99  
   100  	os.Stdout.Write(bs)
   101  }