Black Lives Matter. Support the Equal Justice Initiative.

Source file src/os/exec/read3.go

Documentation: os/exec

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

View as plain text