1 /*	$OpenBSD: sigsuspend.c,v 1.2 2001/11/11 23:26:35 deraadt 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 
74 	while (sigcounts[SIGINT] == 0) {
75 		save_count = sigcounts[SIGUSR2];
76 
77 		status = sigsuspend (&suspender_mask);
78 		if ((status == 0) || (errno != EINTR)) {
79 			DIE(errno, "Unable to suspend for signals, "
80 				"return value %d\n",
81 				status);
82 		}
83 		for (i = 0; i < fifo_depth; i++)
84 			printf ("Sigsuspend woke up by signal %d (%s)\n",
85 				sigfifo[i], strsignal(sigfifo[i]));
86 		fifo_depth = 0;
87 	}
88 
89 	return (arg);
90 }
91 
92 
93 static void
94 sighandler (int signo)
95 {
96 	int save_errno = errno;
97 	char buf[8192];
98 	sigset_t set;
99 	pthread_t self;
100 
101 	if ((signo >= 0) && (signo <= NSIG))
102 		sigcounts[signo]++;
103 
104 	/*
105 	 * If we are running on behalf of the suspender thread,
106 	 * ensure that we have the correct mask set.
107 	 */
108 	self = pthread_self ();
109 	if (self == suspender_tid) {
110 		sigfifo[fifo_depth] = signo;
111 		fifo_depth++;
112 		snprintf(buf, sizeof buf,
113 		    "  -> Suspender thread signal handler caught "
114 			"signal %d (%s)\n", signo, strsignal(signo));
115 		write(STDOUT_FILENO, buf, strlen(buf));
116 		sigprocmask (SIG_SETMASK, NULL, &set);
117 		ASSERT(set == suspender_mask);
118 	} else {
119 		snprintf(buf, sizeof buf,
120 		    "  -> Main thread signal handler caught "
121 			"signal %d (%s)\n", signo, strsignal(signo));
122 		write(STDOUT_FILENO, buf, strlen(buf));
123 	}
124 	errno = save_errno;
125 }
126 
127 
128 int main (int argc, char *argv[])
129 {
130 	pthread_attr_t	pattr;
131 	struct sigaction act;
132 	sigset_t	oldset;
133 	sigset_t	newset;
134 
135 	/* Initialize our signal counts. */
136 	memset ((void *) sigcounts, 0, NSIG * sizeof (int));
137 
138 	/* Ignore signal SIGIO. */
139 	sigemptyset (&act.sa_mask);
140 	sigaddset (&act.sa_mask, SIGIO);
141 	act.sa_handler = SIG_IGN;
142 	act.sa_flags = 0;
143 	CHECKe(sigaction (SIGIO, &act, NULL));
144 
145 	/* Install a signal handler for SIGURG. */
146 	sigemptyset (&act.sa_mask);
147 	sigaddset (&act.sa_mask, SIGURG);
148 	act.sa_handler = sighandler;
149 	act.sa_flags = SA_RESTART;
150 	CHECKe(sigaction (SIGURG, &act, NULL));
151 
152 	/* Install a signal handler for SIGXCPU */
153 	sigemptyset (&act.sa_mask);
154 	sigaddset (&act.sa_mask, SIGXCPU);
155 	CHECKe(sigaction (SIGXCPU, &act, NULL));
156 
157 	/* Get our current signal mask. */
158 	CHECKe(sigprocmask (SIG_SETMASK, NULL, &oldset));
159 
160 	/* Mask out SIGUSR1 and SIGUSR2. */
161 	newset = oldset;
162 	sigaddset (&newset, SIGUSR1);
163 	sigaddset (&newset, SIGUSR2);
164 	CHECKe(sigprocmask (SIG_SETMASK, &newset, NULL));
165 
166 	/* Install a signal handler for SIGUSR1 and SIGUSR2 */
167 	sigemptyset (&act.sa_mask);
168 	sigaddset (&act.sa_mask, SIGUSR1);
169 	sigaddset (&act.sa_mask, SIGUSR2);
170 	act.sa_handler = sighandler;
171 	act.sa_flags = SA_RESTART;
172 	CHECKe(sigaction (SIGUSR1, &act, NULL));
173 	CHECKe(sigaction (SIGUSR2, &act, NULL));
174 
175 	/*
176 	 * Initialize the thread attribute.
177 	 */
178 	CHECKr(pthread_attr_init (&pattr));
179 	CHECKr(pthread_attr_setdetachstate (&pattr, PTHREAD_CREATE_JOINABLE));
180 
181 	/*
182 	 * Create the sigsuspender thread.
183 	 */
184 	CHECKr(pthread_create (&suspender_tid, &pattr, sigsuspender, NULL));
185 
186 	/*
187 	 * Verify that an ignored signal doesn't cause a wakeup.
188 	 * We don't have a handler installed for SIGIO.
189 	 */
190 	CHECKr(pthread_kill (suspender_tid, SIGIO));
191 	sleep (1);
192 	CHECKe(kill (getpid (), SIGIO));
193 	sleep (1);
194 	/* sigsuspend should not wake up for ignored signal SIGIO */
195 	ASSERT(sigcounts[SIGIO] == 0);
196 
197 	/*
198 	 * Verify that a signal with a default action of ignore, for
199 	 * which we have a signal handler installed, will release a
200 	 * sigsuspend.
201 	 */
202 	CHECKr(pthread_kill (suspender_tid, SIGURG));
203 	sleep (1);
204 	CHECKe(kill (getpid (), SIGURG));
205 	sleep (1);
206 	/* sigsuspend should wake up for SIGURG */
207 	ASSERT(sigcounts[SIGURG] == 3);
208 
209 	/*
210 	 * Verify that a SIGUSR2 signal will release a sigsuspended
211 	 * thread.
212 	 */
213 	CHECKr(pthread_kill (suspender_tid, SIGUSR2));
214 	sleep (1);
215 	CHECKe(kill (getpid (), SIGUSR2));
216 	sleep (1);
217 	/* sigsuspend should wake yp for SIGUSR2 */
218 	ASSERT(sigcounts[SIGUSR2] == 2);
219 
220 	/*
221 	 * Verify that a signal, blocked in both the main and
222 	 * sigsuspender threads, does not cause the signal handler
223 	 * to be called.
224 	 */
225 	CHECKr(pthread_kill (suspender_tid, SIGUSR1));
226 	sleep (1);
227 	CHECKe(kill (getpid (), SIGUSR1));
228 	sleep (1);
229 	/* signal handler should not be called for USR1 */
230 	ASSERT(sigcounts[SIGUSR1] == 0);
231 #if 0
232 	/*
233 	 * Verify that we can still kill the process for a signal
234 	 * not being waited on by sigwait.
235 	 */
236 	CHECKe(kill (getpid (), SIGPIPE));
237 	PANIC("SIGPIPE did not terminate process");
238 
239 	/*
240 	 * Wait for the thread to finish.
241 	 */
242 	CHECKr(pthread_join (suspender_tid, NULL));
243 #endif
244 	SUCCEED;
245 }
246 
247