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 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package signal 8 9 import ( 10 "flag" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 "runtime" 15 "strconv" 16 "syscall" 17 "testing" 18 "time" 19 ) 20 21 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { 22 select { 23 case s := <-c: 24 if s != sig { 25 t.Fatalf("signal was %v, want %v", s, sig) 26 } 27 case <-time.After(1 * time.Second): 28 t.Fatalf("timeout waiting for %v", sig) 29 } 30 } 31 32 // Test that basic signal handling works. 33 func TestSignal(t *testing.T) { 34 // Ask for SIGHUP 35 c := make(chan os.Signal, 1) 36 Notify(c, syscall.SIGHUP) 37 defer Stop(c) 38 39 // Send this process a SIGHUP 40 t.Logf("sighup...") 41 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 42 waitSig(t, c, syscall.SIGHUP) 43 44 // Ask for everything we can get. 45 c1 := make(chan os.Signal, 1) 46 Notify(c1) 47 48 // Send this process a SIGWINCH 49 t.Logf("sigwinch...") 50 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 51 waitSig(t, c1, syscall.SIGWINCH) 52 53 // Send two more SIGHUPs, to make sure that 54 // they get delivered on c1 and that not reading 55 // from c does not block everything. 56 t.Logf("sighup...") 57 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 58 waitSig(t, c1, syscall.SIGHUP) 59 t.Logf("sighup...") 60 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 61 waitSig(t, c1, syscall.SIGHUP) 62 63 // The first SIGHUP should be waiting for us on c. 64 waitSig(t, c, syscall.SIGHUP) 65 } 66 67 func TestStress(t *testing.T) { 68 dur := 3 * time.Second 69 if testing.Short() { 70 dur = 100 * time.Millisecond 71 } 72 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 73 done := make(chan bool) 74 finished := make(chan bool) 75 go func() { 76 sig := make(chan os.Signal, 1) 77 Notify(sig, syscall.SIGUSR1) 78 defer Stop(sig) 79 Loop: 80 for { 81 select { 82 case <-sig: 83 case <-done: 84 break Loop 85 } 86 } 87 finished <- true 88 }() 89 go func() { 90 Loop: 91 for { 92 select { 93 case <-done: 94 break Loop 95 default: 96 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 97 runtime.Gosched() 98 } 99 } 100 finished <- true 101 }() 102 time.Sleep(dur) 103 close(done) 104 <-finished 105 <-finished 106 // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip 107 // into subsequent TestSignal() causing failure. 108 // Sleep for a while to reduce the possibility of the failure. 109 time.Sleep(10 * time.Millisecond) 110 } 111 112 func testCancel(t *testing.T, ignore bool) { 113 // Send SIGWINCH. By default this signal should be ignored. 114 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 115 time.Sleep(100 * time.Millisecond) 116 117 // Ask to be notified on c1 when a SIGWINCH is received. 118 c1 := make(chan os.Signal, 1) 119 Notify(c1, syscall.SIGWINCH) 120 defer Stop(c1) 121 122 // Ask to be notified on c2 when a SIGHUP is received. 123 c2 := make(chan os.Signal, 1) 124 Notify(c2, syscall.SIGHUP) 125 defer Stop(c2) 126 127 // Send this process a SIGWINCH and wait for notification on c1. 128 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 129 waitSig(t, c1, syscall.SIGWINCH) 130 131 // Send this process a SIGHUP and wait for notification on c2. 132 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 133 waitSig(t, c2, syscall.SIGHUP) 134 135 // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP. 136 if ignore { 137 Ignore(syscall.SIGWINCH, syscall.SIGHUP) 138 } else { 139 Reset(syscall.SIGWINCH, syscall.SIGHUP) 140 } 141 142 // Send this process a SIGWINCH. It should be ignored. 143 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 144 145 // If ignoring, Send this process a SIGHUP. It should be ignored. 146 if ignore { 147 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 148 } 149 150 select { 151 case s := <-c1: 152 t.Fatalf("unexpected signal %v", s) 153 case <-time.After(100 * time.Millisecond): 154 // nothing to read - good 155 } 156 157 select { 158 case s := <-c2: 159 t.Fatalf("unexpected signal %v", s) 160 case <-time.After(100 * time.Millisecond): 161 // nothing to read - good 162 } 163 164 // Reset the signal handlers for all signals. 165 Reset() 166 } 167 168 // Test that Reset cancels registration for listed signals on all channels. 169 func TestReset(t *testing.T) { 170 testCancel(t, false) 171 } 172 173 // Test that Ignore cancels registration for listed signals on all channels. 174 func TestIgnore(t *testing.T) { 175 testCancel(t, true) 176 } 177 178 var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop") 179 180 // Test that Stop cancels the channel's registrations. 181 func TestStop(t *testing.T) { 182 sigs := []syscall.Signal{ 183 syscall.SIGWINCH, 184 syscall.SIGHUP, 185 } 186 187 for _, sig := range sigs { 188 // Send the signal. 189 // If it's SIGWINCH, we should not see it. 190 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 191 if sig != syscall.SIGHUP || *sendUncaughtSighup == 1 { 192 syscall.Kill(syscall.Getpid(), sig) 193 } 194 time.Sleep(100 * time.Millisecond) 195 196 // Ask for signal 197 c := make(chan os.Signal, 1) 198 Notify(c, sig) 199 defer Stop(c) 200 201 // Send this process that signal 202 syscall.Kill(syscall.Getpid(), sig) 203 waitSig(t, c, sig) 204 205 Stop(c) 206 select { 207 case s := <-c: 208 t.Fatalf("unexpected signal %v", s) 209 case <-time.After(100 * time.Millisecond): 210 // nothing to read - good 211 } 212 213 // Send the signal. 214 // If it's SIGWINCH, we should not see it. 215 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 216 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 { 217 syscall.Kill(syscall.Getpid(), sig) 218 } 219 220 select { 221 case s := <-c: 222 t.Fatalf("unexpected signal %v", s) 223 case <-time.After(100 * time.Millisecond): 224 // nothing to read - good 225 } 226 } 227 } 228 229 // Test that when run under nohup, an uncaught SIGHUP does not kill the program, 230 // but a 231 func TestNohup(t *testing.T) { 232 // Ugly: ask for SIGHUP so that child will not have no-hup set 233 // even if test is running under nohup environment. 234 // We have no intention of reading from c. 235 c := make(chan os.Signal, 1) 236 Notify(c, syscall.SIGHUP) 237 238 // When run without nohup, the test should crash on an uncaught SIGHUP. 239 // When run under nohup, the test should ignore uncaught SIGHUPs, 240 // because the runtime is not supposed to be listening for them. 241 // Either way, TestStop should still be able to catch them when it wants them 242 // and then when it stops wanting them, the original behavior should resume. 243 // 244 // send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs. 245 // send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs. 246 // 247 // Both should fail without nohup and succeed with nohup. 248 249 for i := 1; i <= 2; i++ { 250 out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() 251 if err == nil { 252 t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out) 253 } 254 } 255 256 Stop(c) 257 258 // Again, this time with nohup, assuming we can find it. 259 _, err := os.Stat("/usr/bin/nohup") 260 if err != nil { 261 t.Skip("cannot find nohup; skipping second half of test") 262 } 263 264 for i := 1; i <= 2; i++ { 265 os.Remove("nohup.out") 266 out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() 267 268 data, _ := ioutil.ReadFile("nohup.out") 269 os.Remove("nohup.out") 270 if err != nil { 271 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) 272 } 273 } 274 } 275