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 
46 static int	sigcounts[NSIG + 1];
47 static int	sigfifo[NSIG + 1];
48 static int	fifo_depth = 0;
49 static sigset_t suspender_mask;
50 static pthread_t suspender_tid;
51 
52 
53 static void *
54 sigsuspender (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 
95 static void
96 sighandler (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 
139 int 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