Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 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 | |
Aram Hăvărneanu | 6d0d08b | 2014-01-10 02:49:37 +1100 | [diff] [blame] | 5 | // +build darwin dragonfly freebsd linux netbsd openbsd solaris |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 6 | |
Ian Lance Taylor | 5bcfd88 | 2017-07-13 08:10:43 -0700 | [diff] [blame] | 7 | package signal |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 8 | |
| 9 | import ( |
Ian Lance Taylor | 8ec7a39 | 2017-06-16 09:29:44 -0700 | [diff] [blame] | 10 | "bytes" |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 11 | "flag" |
Ian Lance Taylor | 8ec7a39 | 2017-06-16 09:29:44 -0700 | [diff] [blame] | 12 | "fmt" |
Ian Lance Taylor | b4dd1d9 | 2017-06-24 20:54:47 -0700 | [diff] [blame] | 13 | "internal/testenv" |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 14 | "io/ioutil" |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 15 | "os" |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 16 | "os/exec" |
Dmitriy Vyukov | 91484c6 | 2012-12-28 15:36:06 +0400 | [diff] [blame] | 17 | "runtime" |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 18 | "strconv" |
Ian Lance Taylor | 8ec7a39 | 2017-06-16 09:29:44 -0700 | [diff] [blame] | 19 | "sync" |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 20 | "syscall" |
| 21 | "testing" |
| 22 | "time" |
| 23 | ) |
| 24 | |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 25 | func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { |
| 26 | select { |
| 27 | case s := <-c: |
| 28 | if s != sig { |
| 29 | t.Fatalf("signal was %v, want %v", s, sig) |
| 30 | } |
| 31 | case <-time.After(1 * time.Second): |
| 32 | t.Fatalf("timeout waiting for %v", sig) |
| 33 | } |
| 34 | } |
| 35 | |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 36 | // Test that basic signal handling works. |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 37 | func TestSignal(t *testing.T) { |
| 38 | // Ask for SIGHUP |
| 39 | c := make(chan os.Signal, 1) |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 40 | Notify(c, syscall.SIGHUP) |
| 41 | defer Stop(c) |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 42 | |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 43 | // Send this process a SIGHUP |
Joel Sing | 932428a | 2013-10-07 09:04:20 -0700 | [diff] [blame] | 44 | t.Logf("sighup...") |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 45 | syscall.Kill(syscall.Getpid(), syscall.SIGHUP) |
| 46 | waitSig(t, c, syscall.SIGHUP) |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 47 | |
| 48 | // Ask for everything we can get. |
| 49 | c1 := make(chan os.Signal, 1) |
| 50 | Notify(c1) |
| 51 | |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 52 | // Send this process a SIGWINCH |
Joel Sing | 932428a | 2013-10-07 09:04:20 -0700 | [diff] [blame] | 53 | t.Logf("sigwinch...") |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 54 | syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) |
| 55 | waitSig(t, c1, syscall.SIGWINCH) |
| 56 | |
| 57 | // Send two more SIGHUPs, to make sure that |
| 58 | // they get delivered on c1 and that not reading |
| 59 | // from c does not block everything. |
Joel Sing | 932428a | 2013-10-07 09:04:20 -0700 | [diff] [blame] | 60 | t.Logf("sighup...") |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 61 | syscall.Kill(syscall.Getpid(), syscall.SIGHUP) |
| 62 | waitSig(t, c1, syscall.SIGHUP) |
Joel Sing | 932428a | 2013-10-07 09:04:20 -0700 | [diff] [blame] | 63 | t.Logf("sighup...") |
Russ Cox | 35586f7 | 2012-02-13 13:52:37 -0500 | [diff] [blame] | 64 | syscall.Kill(syscall.Getpid(), syscall.SIGHUP) |
| 65 | waitSig(t, c1, syscall.SIGHUP) |
| 66 | |
| 67 | // The first SIGHUP should be waiting for us on c. |
| 68 | waitSig(t, c, syscall.SIGHUP) |
| 69 | } |
Dmitriy Vyukov | 91484c6 | 2012-12-28 15:36:06 +0400 | [diff] [blame] | 70 | |
| 71 | func TestStress(t *testing.T) { |
| 72 | dur := 3 * time.Second |
| 73 | if testing.Short() { |
| 74 | dur = 100 * time.Millisecond |
| 75 | } |
| 76 | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) |
| 77 | done := make(chan bool) |
| 78 | finished := make(chan bool) |
| 79 | go func() { |
| 80 | sig := make(chan os.Signal, 1) |
| 81 | Notify(sig, syscall.SIGUSR1) |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 82 | defer Stop(sig) |
Dmitriy Vyukov | 91484c6 | 2012-12-28 15:36:06 +0400 | [diff] [blame] | 83 | Loop: |
| 84 | for { |
| 85 | select { |
| 86 | case <-sig: |
| 87 | case <-done: |
| 88 | break Loop |
| 89 | } |
| 90 | } |
| 91 | finished <- true |
| 92 | }() |
| 93 | go func() { |
| 94 | Loop: |
| 95 | for { |
| 96 | select { |
| 97 | case <-done: |
| 98 | break Loop |
| 99 | default: |
| 100 | syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) |
| 101 | runtime.Gosched() |
| 102 | } |
| 103 | } |
| 104 | finished <- true |
| 105 | }() |
| 106 | time.Sleep(dur) |
| 107 | close(done) |
| 108 | <-finished |
| 109 | <-finished |
Dmitriy Vyukov | fe01755 | 2013-03-11 22:31:34 +0400 | [diff] [blame] | 110 | // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip |
| 111 | // into subsequent TestSignal() causing failure. |
| 112 | // Sleep for a while to reduce the possibility of the failure. |
| 113 | time.Sleep(10 * time.Millisecond) |
Dmitriy Vyukov | 91484c6 | 2012-12-28 15:36:06 +0400 | [diff] [blame] | 114 | } |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 115 | |
Michael MacInnis | 194ad16 | 2015-01-29 22:37:41 -0500 | [diff] [blame] | 116 | func testCancel(t *testing.T, ignore bool) { |
| 117 | // Send SIGWINCH. By default this signal should be ignored. |
| 118 | syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) |
| 119 | time.Sleep(100 * time.Millisecond) |
| 120 | |
| 121 | // Ask to be notified on c1 when a SIGWINCH is received. |
| 122 | c1 := make(chan os.Signal, 1) |
| 123 | Notify(c1, syscall.SIGWINCH) |
| 124 | defer Stop(c1) |
| 125 | |
| 126 | // Ask to be notified on c2 when a SIGHUP is received. |
| 127 | c2 := make(chan os.Signal, 1) |
| 128 | Notify(c2, syscall.SIGHUP) |
| 129 | defer Stop(c2) |
| 130 | |
| 131 | // Send this process a SIGWINCH and wait for notification on c1. |
| 132 | syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) |
| 133 | waitSig(t, c1, syscall.SIGWINCH) |
| 134 | |
| 135 | // Send this process a SIGHUP and wait for notification on c2. |
| 136 | syscall.Kill(syscall.Getpid(), syscall.SIGHUP) |
| 137 | waitSig(t, c2, syscall.SIGHUP) |
| 138 | |
| 139 | // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP. |
| 140 | if ignore { |
| 141 | Ignore(syscall.SIGWINCH, syscall.SIGHUP) |
| 142 | } else { |
| 143 | Reset(syscall.SIGWINCH, syscall.SIGHUP) |
| 144 | } |
| 145 | |
Ian Lance Taylor | bc4fdfd | 2016-06-01 09:31:31 -0700 | [diff] [blame] | 146 | // At this point we do not expect any further signals on c1. |
| 147 | // However, it is just barely possible that the initial SIGWINCH |
| 148 | // at the start of this function was delivered after we called |
| 149 | // Notify on c1. In that case the waitSig for SIGWINCH may have |
| 150 | // picked up that initial SIGWINCH, and the second SIGWINCH may |
| 151 | // then have been delivered on the channel. This sequence of events |
| 152 | // may have caused issue 15661. |
| 153 | // So, read any possible signal from the channel now. |
| 154 | select { |
| 155 | case <-c1: |
| 156 | default: |
| 157 | } |
| 158 | |
Michael MacInnis | 194ad16 | 2015-01-29 22:37:41 -0500 | [diff] [blame] | 159 | // Send this process a SIGWINCH. It should be ignored. |
| 160 | syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) |
| 161 | |
| 162 | // If ignoring, Send this process a SIGHUP. It should be ignored. |
| 163 | if ignore { |
| 164 | syscall.Kill(syscall.Getpid(), syscall.SIGHUP) |
| 165 | } |
| 166 | |
| 167 | select { |
| 168 | case s := <-c1: |
| 169 | t.Fatalf("unexpected signal %v", s) |
| 170 | case <-time.After(100 * time.Millisecond): |
| 171 | // nothing to read - good |
| 172 | } |
| 173 | |
| 174 | select { |
| 175 | case s := <-c2: |
| 176 | t.Fatalf("unexpected signal %v", s) |
| 177 | case <-time.After(100 * time.Millisecond): |
| 178 | // nothing to read - good |
| 179 | } |
| 180 | |
| 181 | // Reset the signal handlers for all signals. |
| 182 | Reset() |
| 183 | } |
| 184 | |
| 185 | // Test that Reset cancels registration for listed signals on all channels. |
| 186 | func TestReset(t *testing.T) { |
| 187 | testCancel(t, false) |
| 188 | } |
| 189 | |
| 190 | // Test that Ignore cancels registration for listed signals on all channels. |
| 191 | func TestIgnore(t *testing.T) { |
| 192 | testCancel(t, true) |
| 193 | } |
| 194 | |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 195 | var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop") |
| 196 | |
| 197 | // Test that Stop cancels the channel's registrations. |
| 198 | func TestStop(t *testing.T) { |
| 199 | sigs := []syscall.Signal{ |
| 200 | syscall.SIGWINCH, |
| 201 | syscall.SIGHUP, |
Ian Lance Taylor | e24b244 | 2016-01-04 16:19:38 -0800 | [diff] [blame] | 202 | syscall.SIGUSR1, |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | for _, sig := range sigs { |
| 206 | // Send the signal. |
| 207 | // If it's SIGWINCH, we should not see it. |
| 208 | // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. |
Ian Lance Taylor | e24b244 | 2016-01-04 16:19:38 -0800 | [diff] [blame] | 209 | if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) { |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 210 | syscall.Kill(syscall.Getpid(), sig) |
| 211 | } |
Russ Cox | c1e3320 | 2014-09-16 15:26:00 -0400 | [diff] [blame] | 212 | time.Sleep(100 * time.Millisecond) |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 213 | |
| 214 | // Ask for signal |
| 215 | c := make(chan os.Signal, 1) |
| 216 | Notify(c, sig) |
| 217 | defer Stop(c) |
| 218 | |
| 219 | // Send this process that signal |
| 220 | syscall.Kill(syscall.Getpid(), sig) |
| 221 | waitSig(t, c, sig) |
| 222 | |
| 223 | Stop(c) |
| 224 | select { |
| 225 | case s := <-c: |
| 226 | t.Fatalf("unexpected signal %v", s) |
Russ Cox | c1e3320 | 2014-09-16 15:26:00 -0400 | [diff] [blame] | 227 | case <-time.After(100 * time.Millisecond): |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 228 | // nothing to read - good |
| 229 | } |
| 230 | |
| 231 | // Send the signal. |
| 232 | // If it's SIGWINCH, we should not see it. |
| 233 | // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. |
| 234 | if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 { |
| 235 | syscall.Kill(syscall.Getpid(), sig) |
| 236 | } |
| 237 | |
| 238 | select { |
| 239 | case s := <-c: |
| 240 | t.Fatalf("unexpected signal %v", s) |
Russ Cox | c1e3320 | 2014-09-16 15:26:00 -0400 | [diff] [blame] | 241 | case <-time.After(100 * time.Millisecond): |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 242 | // nothing to read - good |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | // Test that when run under nohup, an uncaught SIGHUP does not kill the program, |
| 248 | // but a |
| 249 | func TestNohup(t *testing.T) { |
| 250 | // Ugly: ask for SIGHUP so that child will not have no-hup set |
| 251 | // even if test is running under nohup environment. |
| 252 | // We have no intention of reading from c. |
| 253 | c := make(chan os.Signal, 1) |
| 254 | Notify(c, syscall.SIGHUP) |
| 255 | |
| 256 | // When run without nohup, the test should crash on an uncaught SIGHUP. |
| 257 | // When run under nohup, the test should ignore uncaught SIGHUPs, |
| 258 | // because the runtime is not supposed to be listening for them. |
| 259 | // Either way, TestStop should still be able to catch them when it wants them |
| 260 | // and then when it stops wanting them, the original behavior should resume. |
| 261 | // |
| 262 | // send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs. |
| 263 | // send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs. |
| 264 | // |
| 265 | // Both should fail without nohup and succeed with nohup. |
| 266 | |
| 267 | for i := 1; i <= 2; i++ { |
| 268 | out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() |
| 269 | if err == nil { |
| 270 | t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out) |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | Stop(c) |
| 275 | |
Aaron Jacobs | acb4765 | 2015-08-25 08:53:42 +1000 | [diff] [blame] | 276 | // Skip the nohup test below when running in tmux on darwin, since nohup |
| 277 | // doesn't work correctly there. See issue #5135. |
| 278 | if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" { |
| 279 | t.Skip("Skipping nohup test due to running in tmux on darwin") |
| 280 | } |
| 281 | |
Russ Cox | cb4428e | 2013-03-15 00:00:02 -0400 | [diff] [blame] | 282 | // Again, this time with nohup, assuming we can find it. |
| 283 | _, err := os.Stat("/usr/bin/nohup") |
| 284 | if err != nil { |
| 285 | t.Skip("cannot find nohup; skipping second half of test") |
| 286 | } |
| 287 | |
| 288 | for i := 1; i <= 2; i++ { |
| 289 | os.Remove("nohup.out") |
| 290 | out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() |
| 291 | |
| 292 | data, _ := ioutil.ReadFile("nohup.out") |
| 293 | os.Remove("nohup.out") |
| 294 | if err != nil { |
| 295 | t.Fatalf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", i, err, out, data) |
| 296 | } |
| 297 | } |
| 298 | } |
Ian Lance Taylor | 81b3511 | 2015-12-31 12:06:31 -0800 | [diff] [blame] | 299 | |
| 300 | // Test that SIGCONT works (issue 8953). |
| 301 | func TestSIGCONT(t *testing.T) { |
| 302 | c := make(chan os.Signal, 1) |
| 303 | Notify(c, syscall.SIGCONT) |
| 304 | defer Stop(c) |
| 305 | syscall.Kill(syscall.Getpid(), syscall.SIGCONT) |
| 306 | waitSig(t, c, syscall.SIGCONT) |
| 307 | } |
Ian Lance Taylor | 8ec7a39 | 2017-06-16 09:29:44 -0700 | [diff] [blame] | 308 | |
| 309 | // Test race between stopping and receiving a signal (issue 14571). |
| 310 | func TestAtomicStop(t *testing.T) { |
| 311 | if os.Getenv("GO_TEST_ATOMIC_STOP") != "" { |
| 312 | atomicStopTestProgram() |
| 313 | t.Fatal("atomicStopTestProgram returned") |
| 314 | } |
| 315 | |
Ian Lance Taylor | b4dd1d9 | 2017-06-24 20:54:47 -0700 | [diff] [blame] | 316 | testenv.MustHaveExec(t) |
| 317 | |
Ian Lance Taylor | 8ec7a39 | 2017-06-16 09:29:44 -0700 | [diff] [blame] | 318 | const execs = 10 |
| 319 | for i := 0; i < execs; i++ { |
| 320 | cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop") |
| 321 | cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1") |
| 322 | out, err := cmd.CombinedOutput() |
| 323 | if err == nil { |
Ian Lance Taylor | eb97160 | 2017-11-22 19:12:12 -0800 | [diff] [blame^] | 324 | if len(out) > 0 { |
| 325 | t.Logf("iteration %d: output %s", i, out) |
| 326 | } |
Ian Lance Taylor | 8ec7a39 | 2017-06-16 09:29:44 -0700 | [diff] [blame] | 327 | } else { |
| 328 | t.Logf("iteration %d: exit status %q: output: %s", i, err, out) |
| 329 | } |
| 330 | |
| 331 | lost := bytes.Contains(out, []byte("lost signal")) |
| 332 | if lost { |
| 333 | t.Errorf("iteration %d: lost signal", i) |
| 334 | } |
| 335 | |
| 336 | // The program should either die due to SIGINT, |
| 337 | // or exit with success without printing "lost signal". |
| 338 | if err == nil { |
| 339 | if len(out) > 0 && !lost { |
| 340 | t.Errorf("iteration %d: unexpected output", i) |
| 341 | } |
| 342 | } else { |
| 343 | if ee, ok := err.(*exec.ExitError); !ok { |
| 344 | t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err) |
| 345 | } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { |
| 346 | t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys()) |
| 347 | } else if !ws.Signaled() || ws.Signal() != syscall.SIGINT { |
| 348 | t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee) |
| 349 | } |
| 350 | } |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | // atomicStopTestProgram is run in a subprocess by TestAtomicStop. |
| 355 | // It tries to trigger a signal delivery race. This function should |
| 356 | // either catch a signal or die from it. |
| 357 | func atomicStopTestProgram() { |
| 358 | const tries = 10 |
| 359 | pid := syscall.Getpid() |
| 360 | printed := false |
| 361 | for i := 0; i < tries; i++ { |
| 362 | cs := make(chan os.Signal, 1) |
| 363 | Notify(cs, syscall.SIGINT) |
| 364 | |
| 365 | var wg sync.WaitGroup |
| 366 | wg.Add(1) |
| 367 | go func() { |
| 368 | defer wg.Done() |
| 369 | Stop(cs) |
| 370 | }() |
| 371 | |
| 372 | syscall.Kill(pid, syscall.SIGINT) |
| 373 | |
| 374 | // At this point we should either die from SIGINT or |
| 375 | // get a notification on cs. If neither happens, we |
| 376 | // dropped the signal. Give it a second to deliver, |
| 377 | // which is far far longer than it should require. |
| 378 | |
| 379 | select { |
| 380 | case <-cs: |
| 381 | case <-time.After(1 * time.Second): |
| 382 | if !printed { |
Ian Lance Taylor | eb97160 | 2017-11-22 19:12:12 -0800 | [diff] [blame^] | 383 | fmt.Print("lost signal on tries:") |
Ian Lance Taylor | 8ec7a39 | 2017-06-16 09:29:44 -0700 | [diff] [blame] | 384 | printed = true |
| 385 | } |
| 386 | fmt.Printf(" %d", i) |
| 387 | } |
| 388 | |
| 389 | wg.Wait() |
| 390 | } |
| 391 | if printed { |
| 392 | fmt.Print("\n") |
| 393 | } |
| 394 | |
| 395 | os.Exit(0) |
| 396 | } |