sigsuspend.c revision 1.5
1/*	$OpenBSD: sigsuspend.c,v 1.5 2012/02/20 01:51:32 guenther Exp $	*/
2/*
3 * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by Daniel M. Eischen.
17 * 4. Neither the name of the author nor the names of any co-contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN AND CONTRIBUTORS ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 */
34#include <stdlib.h>
35#include <unistd.h>
36
37#include <errno.h>
38#include <pthread.h>
39#include <signal.h>
40#include <stdio.h>
41#include <string.h>
42
43#include <pthread_np.h>
44#include "test.h"
45
46static int	sigcounts[NSIG + 1];
47static int	sigfifo[NSIG + 1];
48static int	fifo_depth = 0;
49static sigset_t suspender_mask;
50static pthread_t suspender_tid;
51
52
53static void *
54sigsuspender (void *arg)
55{
56	int save_count, status, i;
57	sigset_t run_mask;
58
59	SET_NAME("sigsuspender");
60
61	/* Run with all signals blocked. */
62	sigfillset (&run_mask);
63	CHECKe(sigprocmask (SIG_SETMASK, &run_mask, NULL));
64
65	/* Allow these signals to wake us up during a sigsuspend. */
66	sigfillset (&suspender_mask);		/* Default action	*/
67	sigdelset (&suspender_mask, SIGINT);	/* terminate		*/
68	sigdelset (&suspender_mask, SIGHUP);	/* terminate		*/
69	sigdelset (&suspender_mask, SIGQUIT);	/* create core image	*/
70	sigdelset (&suspender_mask, SIGURG);	/* ignore		*/
71	sigdelset (&suspender_mask, SIGIO);	/* ignore		*/
72	sigdelset (&suspender_mask, SIGUSR2);	/* terminate		*/
73	sigdelset (&suspender_mask, SIGSTOP);	/* unblockable		*/
74	sigdelset (&suspender_mask, SIGKILL);	/* unblockable		*/
75
76	while (sigcounts[SIGINT] == 0) {
77		save_count = sigcounts[SIGUSR2];
78
79		status = sigsuspend (&suspender_mask);
80		if ((status == 0) || (errno != EINTR)) {
81			DIE(errno, "Unable to suspend for signals, "
82				"return value %d\n",
83				status);
84		}
85		for (i = 0; i < fifo_depth; i++)
86			printf ("Sigsuspend woke up by signal %d (%s)\n",
87				sigfifo[i], strsignal(sigfifo[i]));
88		fifo_depth = 0;
89	}
90
91	return (arg);
92}
93
94
95static void
96sighandler (int signo)
97{
98	int save_errno = errno;
99	char buf[8192];
100	sigset_t set;
101	sigset_t tmp;
102	pthread_t self;
103
104	if ((signo >= 0) && (signo <= NSIG))
105		sigcounts[signo]++;
106
107	/*
108	 * If we are running on behalf of the suspender thread,
109	 * ensure that we have the correct mask set.   NOTE: per
110	 * POSIX the current signo will be part of the mask unless
111	 * SA_NODEFER was specified.   Since it isn't in this test
112	 * add the current signal to the original suspender_mask
113	 * before checking.
114	 */
115	self = pthread_self ();
116	if (self == suspender_tid) {
117		sigfifo[fifo_depth] = signo;
118		fifo_depth++;
119		snprintf(buf, sizeof buf,
120		    "  -> Suspender thread signal handler caught "
121			"signal %d (%s)\n", signo, strsignal(signo));
122		write(STDOUT_FILENO, buf, strlen(buf));
123		sigprocmask (SIG_SETMASK, NULL, &set);
124		tmp = suspender_mask;
125		sigaddset(&tmp, signo);
126		sigdelset(&tmp, SIGTHR);
127		sigdelset(&set, SIGTHR);
128		ASSERT(set == tmp);
129	} else {
130		snprintf(buf, sizeof buf,
131		    "  -> Main thread signal handler caught "
132			"signal %d (%s)\n", signo, strsignal(signo));
133		write(STDOUT_FILENO, buf, strlen(buf));
134	}
135	errno = save_errno;
136}
137
138
139int main (int argc, char *argv[])
140{
141	pthread_attr_t	pattr;
142	struct sigaction act;
143	sigset_t	oldset;
144	sigset_t	newset;
145
146	/* Initialize our signal counts. */
147	memset ((void *) sigcounts, 0, NSIG * sizeof (int));
148
149	/* Ignore signal SIGIO. */
150	sigemptyset (&act.sa_mask);
151	sigaddset (&act.sa_mask, SIGIO);
152	act.sa_handler = SIG_IGN;
153	act.sa_flags = 0;
154	CHECKe(sigaction (SIGIO, &act, NULL));
155
156	/* Install a signal handler for SIGURG. */
157	sigemptyset (&act.sa_mask);
158	sigaddset (&act.sa_mask, SIGURG);
159	act.sa_handler = sighandler;
160	act.sa_flags = SA_RESTART;
161	CHECKe(sigaction (SIGURG, &act, NULL));
162
163	/* Install a signal handler for SIGXCPU */
164	sigemptyset (&act.sa_mask);
165	sigaddset (&act.sa_mask, SIGXCPU);
166	CHECKe(sigaction (SIGXCPU, &act, NULL));
167
168	/* Get our current signal mask. */
169	CHECKe(sigprocmask (SIG_SETMASK, NULL, &oldset));
170
171	/* Mask out SIGUSR1 and SIGUSR2. */
172	newset = oldset;
173	sigaddset (&newset, SIGUSR1);
174	sigaddset (&newset, SIGUSR2);
175	CHECKe(sigprocmask (SIG_SETMASK, &newset, NULL));
176
177	/* Install a signal handler for SIGUSR1 and SIGUSR2 */
178	sigemptyset (&act.sa_mask);
179	sigaddset (&act.sa_mask, SIGUSR1);
180	sigaddset (&act.sa_mask, SIGUSR2);
181	act.sa_handler = sighandler;
182	act.sa_flags = SA_RESTART;
183	CHECKe(sigaction (SIGUSR1, &act, NULL));
184	CHECKe(sigaction (SIGUSR2, &act, NULL));
185
186	/*
187	 * Initialize the thread attribute.
188	 */
189	CHECKr(pthread_attr_init (&pattr));
190	CHECKr(pthread_attr_setdetachstate (&pattr, PTHREAD_CREATE_JOINABLE));
191
192	/*
193	 * Create the sigsuspender thread.
194	 */
195	CHECKr(pthread_create (&suspender_tid, &pattr, sigsuspender, NULL));
196
197	/*
198	 * Verify that an ignored signal doesn't cause a wakeup.
199	 * We don't have a handler installed for SIGIO.
200	 */
201	CHECKr(pthread_kill (suspender_tid, SIGIO));
202	sleep (1);
203	CHECKe(kill (getpid (), SIGIO));
204	sleep (1);
205	/* sigsuspend should not wake up for ignored signal SIGIO */
206	ASSERT(sigcounts[SIGIO] == 0);
207
208	/*
209	 * Verify that a signal with a default action of ignore, for
210	 * which we have a signal handler installed, will release a
211	 * sigsuspend.
212	 */
213	CHECKr(pthread_kill (suspender_tid, SIGURG));
214	sleep (1);
215	CHECKe(kill (getpid (), SIGURG));
216	sleep (1);
217	/* sigsuspend should wake up for SIGURG */
218	ASSERT(sigcounts[SIGURG] == 2);
219
220	/*
221	 * Verify that a SIGUSR2 signal will release a sigsuspended
222	 * thread.
223	 */
224	CHECKr(pthread_kill (suspender_tid, SIGUSR2));
225	sleep (1);
226	CHECKe(kill (getpid (), SIGUSR2));
227	sleep (1);
228	/* sigsuspend should wake up for SIGUSR2 */
229	ASSERT(sigcounts[SIGUSR2] == 2);
230
231	/*
232	 * Verify that a signal, blocked in both the main and
233	 * sigsuspender threads, does not cause the signal handler
234	 * to be called.
235	 */
236	CHECKr(pthread_kill (suspender_tid, SIGUSR1));
237	sleep (1);
238	CHECKe(kill (getpid (), SIGUSR1));
239	sleep (1);
240	/* signal handler should not be called for USR1 */
241	ASSERT(sigcounts[SIGUSR1] == 0);
242#if 0
243	/*
244	 * Verify that we can still kill the process for a signal
245	 * not being waited on by sigwait.
246	 */
247	CHECKe(kill (getpid (), SIGPIPE));
248	PANIC("SIGPIPE did not terminate process");
249
250	/*
251	 * Wait for the thread to finish.
252	 */
253	CHECKr(pthread_join (suspender_tid, NULL));
254#endif
255	SUCCEED;
256}
257
258