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 aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
6
7package signal
8
9import (
10	"bytes"
11	"flag"
12	"fmt"
13	"internal/testenv"
14	"io/ioutil"
15	"os"
16	"os/exec"
17	"runtime"
18	"strconv"
19	"sync"
20	"syscall"
21	"testing"
22	"time"
23)
24
25var testDeadline time.Time
26
27func TestMain(m *testing.M) {
28	flag.Parse()
29
30	// TODO(golang.org/issue/28135): Remove this setup and use t.Deadline instead.
31	timeoutFlag := flag.Lookup("test.timeout")
32	if timeoutFlag != nil {
33		if d := timeoutFlag.Value.(flag.Getter).Get().(time.Duration); d != 0 {
34			testDeadline = time.Now().Add(d)
35		}
36	}
37
38	os.Exit(m.Run())
39}
40
41func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
42	waitSig1(t, c, sig, false)
43}
44func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) {
45	waitSig1(t, c, sig, true)
46}
47
48func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
49	// Sleep multiple times to give the kernel more tries to
50	// deliver the signal.
51	for i := 0; i < 10; i++ {
52		select {
53		case s := <-c:
54			// If the caller notified for all signals on
55			// c, filter out SIGURG, which is used for
56			// runtime preemption and can come at
57			// unpredictable times.
58			if all && s == syscall.SIGURG {
59				continue
60			}
61			if s != sig {
62				t.Fatalf("signal was %v, want %v", s, sig)
63			}
64			return
65
66		case <-time.After(100 * time.Millisecond):
67		}
68	}
69	t.Fatalf("timeout waiting for %v", sig)
70}
71
72// Test that basic signal handling works.
73func TestSignal(t *testing.T) {
74	// Ask for SIGHUP
75	c := make(chan os.Signal, 1)
76	Notify(c, syscall.SIGHUP)
77	defer Stop(c)
78
79	// Send this process a SIGHUP
80	t.Logf("sighup...")
81	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
82	waitSig(t, c, syscall.SIGHUP)
83
84	// Ask for everything we can get. The buffer size has to be
85	// more than 1, since the runtime might send SIGURG signals.
86	// Using 10 is arbitrary.
87	c1 := make(chan os.Signal, 10)
88	Notify(c1)
89
90	// Send this process a SIGWINCH
91	t.Logf("sigwinch...")
92	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
93	waitSigAll(t, c1, syscall.SIGWINCH)
94
95	// Send two more SIGHUPs, to make sure that
96	// they get delivered on c1 and that not reading
97	// from c does not block everything.
98	t.Logf("sighup...")
99	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
100	waitSigAll(t, c1, syscall.SIGHUP)
101	t.Logf("sighup...")
102	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
103	waitSigAll(t, c1, syscall.SIGHUP)
104
105	// The first SIGHUP should be waiting for us on c.
106	waitSig(t, c, syscall.SIGHUP)
107}
108
109func TestStress(t *testing.T) {
110	dur := 3 * time.Second
111	if testing.Short() {
112		dur = 100 * time.Millisecond
113	}
114	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
115	done := make(chan bool)
116	finished := make(chan bool)
117	go func() {
118		sig := make(chan os.Signal, 1)
119		Notify(sig, syscall.SIGUSR1)
120		defer Stop(sig)
121	Loop:
122		for {
123			select {
124			case <-sig:
125			case <-done:
126				break Loop
127			}
128		}
129		finished <- true
130	}()
131	go func() {
132	Loop:
133		for {
134			select {
135			case <-done:
136				break Loop
137			default:
138				syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
139				runtime.Gosched()
140			}
141		}
142		finished <- true
143	}()
144	time.Sleep(dur)
145	close(done)
146	<-finished
147	<-finished
148	// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
149	// into subsequent TestSignal() causing failure.
150	// Sleep for a while to reduce the possibility of the failure.
151	time.Sleep(10 * time.Millisecond)
152}
153
154func testCancel(t *testing.T, ignore bool) {
155	// Send SIGWINCH. By default this signal should be ignored.
156	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
157	time.Sleep(100 * time.Millisecond)
158
159	// Ask to be notified on c1 when a SIGWINCH is received.
160	c1 := make(chan os.Signal, 1)
161	Notify(c1, syscall.SIGWINCH)
162	defer Stop(c1)
163
164	// Ask to be notified on c2 when a SIGHUP is received.
165	c2 := make(chan os.Signal, 1)
166	Notify(c2, syscall.SIGHUP)
167	defer Stop(c2)
168
169	// Send this process a SIGWINCH and wait for notification on c1.
170	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
171	waitSig(t, c1, syscall.SIGWINCH)
172
173	// Send this process a SIGHUP and wait for notification on c2.
174	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
175	waitSig(t, c2, syscall.SIGHUP)
176
177	// Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
178	if ignore {
179		Ignore(syscall.SIGWINCH, syscall.SIGHUP)
180	} else {
181		Reset(syscall.SIGWINCH, syscall.SIGHUP)
182	}
183
184	// At this point we do not expect any further signals on c1.
185	// However, it is just barely possible that the initial SIGWINCH
186	// at the start of this function was delivered after we called
187	// Notify on c1. In that case the waitSig for SIGWINCH may have
188	// picked up that initial SIGWINCH, and the second SIGWINCH may
189	// then have been delivered on the channel. This sequence of events
190	// may have caused issue 15661.
191	// So, read any possible signal from the channel now.
192	select {
193	case <-c1:
194	default:
195	}
196
197	// Send this process a SIGWINCH. It should be ignored.
198	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
199
200	// If ignoring, Send this process a SIGHUP. It should be ignored.
201	if ignore {
202		syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
203	}
204
205	select {
206	case s := <-c1:
207		t.Fatalf("unexpected signal %v", s)
208	case <-time.After(100 * time.Millisecond):
209		// nothing to read - good
210	}
211
212	select {
213	case s := <-c2:
214		t.Fatalf("unexpected signal %v", s)
215	case <-time.After(100 * time.Millisecond):
216		// nothing to read - good
217	}
218
219	// Reset the signal handlers for all signals.
220	Reset()
221}
222
223// Test that Reset cancels registration for listed signals on all channels.
224func TestReset(t *testing.T) {
225	testCancel(t, false)
226}
227
228// Test that Ignore cancels registration for listed signals on all channels.
229func TestIgnore(t *testing.T) {
230	testCancel(t, true)
231}
232
233// Test that Ignored correctly detects changes to the ignored status of a signal.
234func TestIgnored(t *testing.T) {
235	// Ask to be notified on SIGWINCH.
236	c := make(chan os.Signal, 1)
237	Notify(c, syscall.SIGWINCH)
238
239	// If we're being notified, then the signal should not be ignored.
240	if Ignored(syscall.SIGWINCH) {
241		t.Errorf("expected SIGWINCH to not be ignored.")
242	}
243	Stop(c)
244	Ignore(syscall.SIGWINCH)
245
246	// We're no longer paying attention to this signal.
247	if !Ignored(syscall.SIGWINCH) {
248		t.Errorf("expected SIGWINCH to be ignored when explicitly ignoring it.")
249	}
250
251	Reset()
252}
253
254var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.")
255
256// Test that Ignored(SIGHUP) correctly detects whether it is being run under nohup.
257func TestDetectNohup(t *testing.T) {
258	if *checkSighupIgnored {
259		if !Ignored(syscall.SIGHUP) {
260			t.Fatal("SIGHUP is not ignored.")
261		} else {
262			t.Log("SIGHUP is ignored.")
263		}
264	} else {
265		defer Reset()
266		// Ugly: ask for SIGHUP so that child will not have no-hup set
267		// even if test is running under nohup environment.
268		// We have no intention of reading from c.
269		c := make(chan os.Signal, 1)
270		Notify(c, syscall.SIGHUP)
271		if out, err := exec.Command(os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput(); err == nil {
272			t.Errorf("ran test with -check_sighup_ignored and it succeeded: expected failure.\nOutput:\n%s", out)
273		}
274		Stop(c)
275		// Again, this time with nohup, assuming we can find it.
276		_, err := os.Stat("/usr/bin/nohup")
277		if err != nil {
278			t.Skip("cannot find nohup; skipping second half of test")
279		}
280		Ignore(syscall.SIGHUP)
281		os.Remove("nohup.out")
282		out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput()
283
284		data, _ := ioutil.ReadFile("nohup.out")
285		os.Remove("nohup.out")
286		if err != nil {
287			t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data)
288		}
289	}
290}
291
292var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
293
294// Test that Stop cancels the channel's registrations.
295func TestStop(t *testing.T) {
296	sigs := []syscall.Signal{
297		syscall.SIGWINCH,
298		syscall.SIGHUP,
299		syscall.SIGUSR1,
300	}
301
302	for _, sig := range sigs {
303		// Send the signal.
304		// If it's SIGWINCH, we should not see it.
305		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
306		if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
307			syscall.Kill(syscall.Getpid(), sig)
308		}
309
310		// The kernel will deliver a signal as a thread returns
311		// from a syscall. If the only active thread is sleeping,
312		// and the system is busy, the kernel may not get around
313		// to waking up a thread to catch the signal.
314		// We try splitting up the sleep to give the kernel
315		// another chance to deliver the signal.
316		time.Sleep(50 * time.Millisecond)
317		time.Sleep(50 * time.Millisecond)
318
319		// Ask for signal
320		c := make(chan os.Signal, 1)
321		Notify(c, sig)
322		defer Stop(c)
323
324		// Send this process that signal
325		syscall.Kill(syscall.Getpid(), sig)
326		waitSig(t, c, sig)
327
328		Stop(c)
329		time.Sleep(50 * time.Millisecond)
330		select {
331		case s := <-c:
332			t.Fatalf("unexpected signal %v", s)
333		case <-time.After(50 * time.Millisecond):
334			// nothing to read - good
335		}
336
337		// Send the signal.
338		// If it's SIGWINCH, we should not see it.
339		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
340		if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
341			syscall.Kill(syscall.Getpid(), sig)
342		}
343
344		time.Sleep(50 * time.Millisecond)
345		select {
346		case s := <-c:
347			t.Fatalf("unexpected signal %v", s)
348		case <-time.After(50 * time.Millisecond):
349			// nothing to read - good
350		}
351	}
352}
353
354// Test that when run under nohup, an uncaught SIGHUP does not kill the program,
355// but a
356func TestNohup(t *testing.T) {
357	// Ugly: ask for SIGHUP so that child will not have no-hup set
358	// even if test is running under nohup environment.
359	// We have no intention of reading from c.
360	c := make(chan os.Signal, 1)
361	Notify(c, syscall.SIGHUP)
362
363	// When run without nohup, the test should crash on an uncaught SIGHUP.
364	// When run under nohup, the test should ignore uncaught SIGHUPs,
365	// because the runtime is not supposed to be listening for them.
366	// Either way, TestStop should still be able to catch them when it wants them
367	// and then when it stops wanting them, the original behavior should resume.
368	//
369	// send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs.
370	// send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs.
371	//
372	// Both should fail without nohup and succeed with nohup.
373
374	for i := 1; i <= 2; i++ {
375		out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
376		if err == nil {
377			t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
378		}
379	}
380
381	Stop(c)
382
383	// Skip the nohup test below when running in tmux on darwin, since nohup
384	// doesn't work correctly there. See issue #5135.
385	if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" {
386		t.Skip("Skipping nohup test due to running in tmux on darwin")
387	}
388
389	// Again, this time with nohup, assuming we can find it.
390	_, err := os.Stat("/usr/bin/nohup")
391	if err != nil {
392		t.Skip("cannot find nohup; skipping second half of test")
393	}
394
395	for i := 1; i <= 2; i++ {
396		os.Remove("nohup.out")
397		out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
398
399		data, _ := ioutil.ReadFile("nohup.out")
400		os.Remove("nohup.out")
401		if err != nil {
402			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)
403		}
404	}
405}
406
407// Test that SIGCONT works (issue 8953).
408func TestSIGCONT(t *testing.T) {
409	c := make(chan os.Signal, 1)
410	Notify(c, syscall.SIGCONT)
411	defer Stop(c)
412	syscall.Kill(syscall.Getpid(), syscall.SIGCONT)
413	waitSig(t, c, syscall.SIGCONT)
414}
415
416// Test race between stopping and receiving a signal (issue 14571).
417func TestAtomicStop(t *testing.T) {
418	if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
419		atomicStopTestProgram()
420		t.Fatal("atomicStopTestProgram returned")
421	}
422
423	testenv.MustHaveExec(t)
424
425	// Call Notify for SIGINT before starting the child process.
426	// That ensures that SIGINT is not ignored for the child.
427	// This is necessary because if SIGINT is ignored when a
428	// Go program starts, then it remains ignored, and closing
429	// the last notification channel for SIGINT will switch it
430	// back to being ignored. In that case the assumption of
431	// atomicStopTestProgram, that it will either die from SIGINT
432	// or have it be reported, breaks down, as there is a third
433	// option: SIGINT might be ignored.
434	cs := make(chan os.Signal, 1)
435	Notify(cs, syscall.SIGINT)
436	defer Stop(cs)
437
438	const execs = 10
439	for i := 0; i < execs; i++ {
440		timeout := "0"
441		if !testDeadline.IsZero() {
442			timeout = testDeadline.Sub(time.Now()).String()
443		}
444		cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop", "-test.timeout="+timeout)
445		cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
446		out, err := cmd.CombinedOutput()
447		if err == nil {
448			if len(out) > 0 {
449				t.Logf("iteration %d: output %s", i, out)
450			}
451		} else {
452			t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
453		}
454
455		lost := bytes.Contains(out, []byte("lost signal"))
456		if lost {
457			t.Errorf("iteration %d: lost signal", i)
458		}
459
460		// The program should either die due to SIGINT,
461		// or exit with success without printing "lost signal".
462		if err == nil {
463			if len(out) > 0 && !lost {
464				t.Errorf("iteration %d: unexpected output", i)
465			}
466		} else {
467			if ee, ok := err.(*exec.ExitError); !ok {
468				t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err)
469			} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
470				t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys())
471			} else if !ws.Signaled() || ws.Signal() != syscall.SIGINT {
472				t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee)
473			}
474		}
475	}
476}
477
478// atomicStopTestProgram is run in a subprocess by TestAtomicStop.
479// It tries to trigger a signal delivery race. This function should
480// either catch a signal or die from it.
481func atomicStopTestProgram() {
482	// This test won't work if SIGINT is ignored here.
483	if Ignored(syscall.SIGINT) {
484		fmt.Println("SIGINT is ignored")
485		os.Exit(1)
486	}
487
488	const tries = 10
489
490	timeout := 2 * time.Second
491	if !testDeadline.IsZero() {
492		// Give each try an equal slice of the deadline, with one slice to spare for
493		// cleanup.
494		timeout = testDeadline.Sub(time.Now()) / (tries + 1)
495	}
496
497	pid := syscall.Getpid()
498	printed := false
499	for i := 0; i < tries; i++ {
500		cs := make(chan os.Signal, 1)
501		Notify(cs, syscall.SIGINT)
502
503		var wg sync.WaitGroup
504		wg.Add(1)
505		go func() {
506			defer wg.Done()
507			Stop(cs)
508		}()
509
510		syscall.Kill(pid, syscall.SIGINT)
511
512		// At this point we should either die from SIGINT or
513		// get a notification on cs. If neither happens, we
514		// dropped the signal. It is given 2 seconds to
515		// deliver, as needed for gccgo on some loaded test systems.
516
517		select {
518		case <-cs:
519		case <-time.After(timeout):
520			if !printed {
521				fmt.Print("lost signal on tries:")
522				printed = true
523			}
524			fmt.Printf(" %d", i)
525		}
526
527		wg.Wait()
528	}
529	if printed {
530		fmt.Print("\n")
531	}
532
533	os.Exit(0)
534}
535
536func TestTime(t *testing.T) {
537	// Test that signal works fine when we are in a call to get time,
538	// which on some platforms is using VDSO. See issue #34391.
539	dur := 3 * time.Second
540	if testing.Short() {
541		dur = 100 * time.Millisecond
542	}
543	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
544	done := make(chan bool)
545	finished := make(chan bool)
546	go func() {
547		sig := make(chan os.Signal, 1)
548		Notify(sig, syscall.SIGUSR1)
549		defer Stop(sig)
550	Loop:
551		for {
552			select {
553			case <-sig:
554			case <-done:
555				break Loop
556			}
557		}
558		finished <- true
559	}()
560	go func() {
561	Loop:
562		for {
563			select {
564			case <-done:
565				break Loop
566			default:
567				syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
568				runtime.Gosched()
569			}
570		}
571		finished <- true
572	}()
573	t0 := time.Now()
574	for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
575	} // hammering on getting time
576	close(done)
577	<-finished
578	<-finished
579	// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
580	// into subsequent TestSignal() causing failure.
581	// Sleep for a while to reduce the possibility of the failure.
582	time.Sleep(10 * time.Millisecond)
583}
584