1 // Copyright 2015 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 // Test broken pipes on Unix systems. 6 // +build !windows,!plan9,!nacl 7 8 package os_test 9 10 import ( 11 "fmt" 12 "internal/testenv" 13 "io" 14 "io/ioutil" 15 "os" 16 osexec "os/exec" 17 "os/signal" 18 "runtime" 19 "strconv" 20 "strings" 21 "sync" 22 "syscall" 23 "testing" 24 "time" 25 ) 26 27 func TestEPIPE(t *testing.T) { 28 r, w, err := os.Pipe() 29 if err != nil { 30 t.Fatal(err) 31 } 32 if err := r.Close(); err != nil { 33 t.Fatal(err) 34 } 35 36 // Every time we write to the pipe we should get an EPIPE. 37 for i := 0; i < 20; i++ { 38 _, err = w.Write([]byte("hi")) 39 if err == nil { 40 t.Fatal("unexpected success of Write to broken pipe") 41 } 42 if pe, ok := err.(*os.PathError); ok { 43 err = pe.Err 44 } 45 if se, ok := err.(*os.SyscallError); ok { 46 err = se.Err 47 } 48 if err != syscall.EPIPE { 49 t.Errorf("iteration %d: got %v, expected EPIPE", i, err) 50 } 51 } 52 } 53 54 func TestStdPipe(t *testing.T) { 55 testenv.MustHaveExec(t) 56 r, w, err := os.Pipe() 57 if err != nil { 58 t.Fatal(err) 59 } 60 if err := r.Close(); err != nil { 61 t.Fatal(err) 62 } 63 // Invoke the test program to run the test and write to a closed pipe. 64 // If sig is false: 65 // writing to stdout or stderr should cause an immediate SIGPIPE; 66 // writing to descriptor 3 should fail with EPIPE and then exit 0. 67 // If sig is true: 68 // all writes should fail with EPIPE and then exit 0. 69 for _, sig := range []bool{false, true} { 70 for dest := 1; dest < 4; dest++ { 71 cmd := osexec.Command(os.Args[0], "-test.run", "TestStdPipeHelper") 72 cmd.Stdout = w 73 cmd.Stderr = w 74 cmd.ExtraFiles = []*os.File{w} 75 cmd.Env = append(os.Environ(), fmt.Sprintf("GO_TEST_STD_PIPE_HELPER=%d", dest)) 76 if sig { 77 cmd.Env = append(cmd.Env, "GO_TEST_STD_PIPE_HELPER_SIGNAL=1") 78 } 79 if err := cmd.Run(); err == nil { 80 if !sig && dest < 3 { 81 t.Errorf("unexpected success of write to closed pipe %d sig %t in child", dest, sig) 82 } 83 } else if ee, ok := err.(*osexec.ExitError); !ok { 84 t.Errorf("unexpected exec error type %T: %v", err, err) 85 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 86 t.Errorf("unexpected wait status type %T: %v", ee.Sys(), ee.Sys()) 87 } else if ws.Signaled() && ws.Signal() == syscall.SIGPIPE { 88 if sig || dest > 2 { 89 t.Errorf("unexpected SIGPIPE signal for descriptor %d sig %t", dest, sig) 90 } 91 } else { 92 t.Errorf("unexpected exit status %v for descriptor %d sig %t", err, dest, sig) 93 } 94 } 95 } 96 } 97 98 // This is a helper for TestStdPipe. It's not a test in itself. 99 func TestStdPipeHelper(t *testing.T) { 100 if os.Getenv("GO_TEST_STD_PIPE_HELPER_SIGNAL") != "" { 101 signal.Notify(make(chan os.Signal, 1), syscall.SIGPIPE) 102 } 103 switch os.Getenv("GO_TEST_STD_PIPE_HELPER") { 104 case "1": 105 os.Stdout.Write([]byte("stdout")) 106 case "2": 107 os.Stderr.Write([]byte("stderr")) 108 case "3": 109 if _, err := os.NewFile(3, "3").Write([]byte("3")); err == nil { 110 os.Exit(3) 111 } 112 default: 113 t.Skip("skipping test helper") 114 } 115 // For stdout/stderr, we should have crashed with a broken pipe error. 116 // The caller will be looking for that exit status, 117 // so just exit normally here to cause a failure in the caller. 118 // For descriptor 3, a normal exit is expected. 119 os.Exit(0) 120 } 121 122 func testClosedPipeRace(t *testing.T, read bool) { 123 switch runtime.GOOS { 124 case "freebsd": 125 t.Skip("FreeBSD does not use the poller; issue 19093") 126 } 127 128 limit := 1 129 if !read { 130 // Get the amount we have to write to overload a pipe 131 // with no reader. 132 limit = 65537 133 if b, err := ioutil.ReadFile("/proc/sys/fs/pipe-max-size"); err == nil { 134 if i, err := strconv.Atoi(strings.TrimSpace(string(b))); err == nil { 135 limit = i + 1 136 } 137 } 138 t.Logf("using pipe write limit of %d", limit) 139 } 140 141 r, w, err := os.Pipe() 142 if err != nil { 143 t.Fatal(err) 144 } 145 defer r.Close() 146 defer w.Close() 147 148 // Close the read end of the pipe in a goroutine while we are 149 // writing to the write end, or vice-versa. 150 go func() { 151 // Give the main goroutine a chance to enter the Read or 152 // Write call. This is sloppy but the test will pass even 153 // if we close before the read/write. 154 time.Sleep(20 * time.Millisecond) 155 156 var err error 157 if read { 158 err = r.Close() 159 } else { 160 err = w.Close() 161 } 162 if err != nil { 163 t.Error(err) 164 } 165 }() 166 167 b := make([]byte, limit) 168 if read { 169 _, err = r.Read(b[:]) 170 } else { 171 _, err = w.Write(b[:]) 172 } 173 if err == nil { 174 t.Error("I/O on closed pipe unexpectedly succeeded") 175 } else if pe, ok := err.(*os.PathError); !ok { 176 t.Errorf("I/O on closed pipe returned unexpected error type %T; expected os.PathError", pe) 177 } else if pe.Err != os.ErrClosed { 178 t.Errorf("got error %q but expected %q", pe.Err, os.ErrClosed) 179 } else { 180 t.Logf("I/O returned expected error %q", err) 181 } 182 } 183 184 func TestClosedPipeRaceRead(t *testing.T) { 185 testClosedPipeRace(t, true) 186 } 187 188 func TestClosedPipeRaceWrite(t *testing.T) { 189 testClosedPipeRace(t, false) 190 } 191 192 // Issue 20915: Reading on nonblocking fd should not return "waiting 193 // for unsupported file type." Currently it returns EAGAIN; it is 194 // possible that in the future it will simply wait for data. 195 func TestReadNonblockingFd(t *testing.T) { 196 if os.Getenv("GO_WANT_READ_NONBLOCKING_FD") == "1" { 197 fd := int(os.Stdin.Fd()) 198 syscall.SetNonblock(fd, true) 199 defer syscall.SetNonblock(fd, false) 200 _, err := os.Stdin.Read(make([]byte, 1)) 201 if err != nil { 202 if perr, ok := err.(*os.PathError); !ok || perr.Err != syscall.EAGAIN { 203 t.Fatalf("read on nonblocking stdin got %q, should have gotten EAGAIN", err) 204 } 205 } 206 os.Exit(0) 207 } 208 209 testenv.MustHaveExec(t) 210 r, w, err := os.Pipe() 211 if err != nil { 212 t.Fatal(err) 213 } 214 defer r.Close() 215 defer w.Close() 216 cmd := osexec.Command(os.Args[0], "-test.run="+t.Name()) 217 cmd.Env = append(os.Environ(), "GO_WANT_READ_NONBLOCKING_FD=1") 218 cmd.Stdin = r 219 output, err := cmd.CombinedOutput() 220 t.Logf("%s", output) 221 if err != nil { 222 t.Errorf("child process failed: %v", err) 223 } 224 } 225 226 func TestCloseWithBlockingReadByNewFile(t *testing.T) { 227 var p [2]int 228 err := syscall.Pipe(p[:]) 229 if err != nil { 230 t.Fatal(err) 231 } 232 // os.NewFile returns a blocking mode file. 233 testCloseWithBlockingRead(t, os.NewFile(uintptr(p[0]), "reader"), os.NewFile(uintptr(p[1]), "writer")) 234 } 235 236 func TestCloseWithBlockingReadByFd(t *testing.T) { 237 r, w, err := os.Pipe() 238 if err != nil { 239 t.Fatal(err) 240 } 241 // Calling Fd will put the file into blocking mode. 242 _ = r.Fd() 243 testCloseWithBlockingRead(t, r, w) 244 } 245 246 // Test that we don't let a blocking read prevent a close. 247 func testCloseWithBlockingRead(t *testing.T, r, w *os.File) { 248 defer r.Close() 249 defer w.Close() 250 251 c1, c2 := make(chan bool), make(chan bool) 252 var wg sync.WaitGroup 253 254 wg.Add(1) 255 go func(c chan bool) { 256 defer wg.Done() 257 // Give the other goroutine a chance to enter the Read 258 // or Write call. This is sloppy but the test will 259 // pass even if we close before the read/write. 260 time.Sleep(20 * time.Millisecond) 261 262 if err := r.Close(); err != nil { 263 t.Error(err) 264 } 265 close(c) 266 }(c1) 267 268 wg.Add(1) 269 go func(c chan bool) { 270 defer wg.Done() 271 var b [1]byte 272 _, err := r.Read(b[:]) 273 close(c) 274 if err == nil { 275 t.Error("I/O on closed pipe unexpectedly succeeded") 276 } 277 if err != io.EOF { 278 t.Errorf("got %v, expected io.EOF", err) 279 } 280 }(c2) 281 282 for c1 != nil || c2 != nil { 283 select { 284 case <-c1: 285 c1 = nil 286 // r.Close has completed, but the blocking Read 287 // is hanging. Close the writer to unblock it. 288 w.Close() 289 case <-c2: 290 c2 = nil 291 case <-time.After(1 * time.Second): 292 switch { 293 case c1 != nil && c2 != nil: 294 t.Error("timed out waiting for Read and Close") 295 w.Close() 296 case c1 != nil: 297 t.Error("timed out waiting for Close") 298 case c2 != nil: 299 t.Error("timed out waiting for Read") 300 default: 301 t.Error("impossible case") 302 } 303 } 304 } 305 306 wg.Wait() 307 } 308