1/*
2 * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Daniel M. Eischen.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN AND CONTRIBUTORS ``AS IS''
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD$
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#if defined(_LIBC_R_)
44#include <pthread_np.h>
45#endif
46
47static int		sigcounts[NSIG + 1];
48static sigset_t		wait_mask;
49static pthread_mutex_t	waiter_mutex;
50
51
52static void *
53sigwaiter (void *arg)
54{
55	int signo;
56	sigset_t mask;
57
58	/* Block SIGHUP */
59	sigemptyset (&mask);
60	sigaddset (&mask, SIGHUP);
61	sigprocmask (SIG_BLOCK, &mask, NULL);
62
63	while (sigcounts[SIGINT] == 0) {
64		if (sigwait (&wait_mask, &signo) != 0) {
65			fprintf (stderr,
66			    "Unable to wait for signal, errno %d\n",
67			    errno);
68			exit (1);
69		}
70		sigcounts[signo]++;
71		fprintf (stderr, "Sigwait caught signal %d\n", signo);
72
73		/* Allow the main thread to prevent the sigwait. */
74		pthread_mutex_lock (&waiter_mutex);
75		pthread_mutex_unlock (&waiter_mutex);
76	}
77
78	pthread_exit (arg);
79	return (NULL);
80}
81
82
83static void
84sighandler (int signo)
85{
86	fprintf (stderr, "  -> Signal handler caught signal %d\n", signo);
87
88	if ((signo >= 0) && (signo <= NSIG))
89		sigcounts[signo]++;
90}
91
92static void
93send_thread_signal (pthread_t tid, int signo)
94{
95	if (pthread_kill (tid, signo) != 0) {
96		fprintf (stderr, "Unable to send thread signal, errno %d.\n",
97		    errno);
98		exit (1);
99	}
100}
101
102static void
103send_process_signal (int signo)
104{
105	if (kill (getpid (), signo) != 0) {
106		fprintf (stderr, "Unable to send process signal, errno %d.\n",
107		    errno);
108		exit (1);
109	}
110}
111
112
113int main (int argc, char *argv[])
114{
115	pthread_mutexattr_t mattr;
116	pthread_attr_t	pattr;
117	pthread_t	tid;
118	void *		exit_status;
119	struct sigaction act;
120
121	/* Initialize our signal counts. */
122	memset ((void *) sigcounts, 0, NSIG * sizeof (int));
123
124	/* Setup our wait mask. */
125	sigemptyset (&wait_mask);		/* Default action	*/
126	sigaddset (&wait_mask, SIGHUP);		/* terminate		*/
127	sigaddset (&wait_mask, SIGINT);		/* terminate		*/
128	sigaddset (&wait_mask, SIGQUIT);	/* create core image	*/
129	sigaddset (&wait_mask, SIGURG);		/* ignore		*/
130	sigaddset (&wait_mask, SIGIO);		/* ignore		*/
131	sigaddset (&wait_mask, SIGUSR1);	/* terminate		*/
132
133	/* Ignore signals SIGHUP and SIGIO. */
134	sigemptyset (&act.sa_mask);
135	sigaddset (&act.sa_mask, SIGHUP);
136	sigaddset (&act.sa_mask, SIGIO);
137	act.sa_handler = SIG_IGN;
138	act.sa_flags = 0;
139	sigaction (SIGHUP, &act, NULL);
140	sigaction (SIGIO, &act, NULL);
141
142	/* Install a signal handler for SIGURG */
143	sigemptyset (&act.sa_mask);
144	sigaddset (&act.sa_mask, SIGURG);
145	act.sa_handler = sighandler;
146	act.sa_flags = SA_RESTART;
147	sigaction (SIGURG, &act, NULL);
148
149	/* Install a signal handler for SIGXCPU */
150	sigemptyset (&act.sa_mask);
151	sigaddset (&act.sa_mask, SIGXCPU);
152	sigaction (SIGXCPU, &act, NULL);
153
154	/*
155	 * Initialize the thread attribute.
156	 */
157	if ((pthread_attr_init (&pattr) != 0) ||
158	    (pthread_attr_setdetachstate (&pattr,
159	    PTHREAD_CREATE_JOINABLE) != 0)) {
160		fprintf (stderr, "Unable to initialize thread attributes.\n");
161		exit (1);
162	}
163
164	/*
165	 * Initialize and create a mutex.
166	 */
167	if ((pthread_mutexattr_init (&mattr) != 0) ||
168	    (pthread_mutex_init (&waiter_mutex, &mattr) != 0)) {
169		fprintf (stderr, "Unable to create waiter mutex.\n");
170		exit (1);
171	}
172
173	/*
174	 * Create the sigwaiter thread.
175	 */
176	if (pthread_create (&tid, &pattr, sigwaiter, NULL) != 0) {
177		fprintf (stderr, "Unable to create thread.\n");
178		exit (1);
179	}
180#if defined(_LIBC_R_)
181	pthread_set_name_np (tid, "sigwaiter");
182#endif
183
184	/*
185	 * Verify that an ignored signal doesn't cause a wakeup.
186	 * We don't have a handler installed for SIGIO.
187	 */
188	send_thread_signal (tid, SIGIO);
189	sleep (1);
190	send_process_signal (SIGIO);
191	sleep (1);
192	if (sigcounts[SIGIO] != 0)
193		fprintf (stderr,
194		    "FAIL: sigwait wakes up for ignored signal SIGIO.\n");
195
196	/*
197	 * Verify that a signal with a default action of ignore, for
198	 * which we have a signal handler installed, will release a sigwait.
199	 */
200	send_thread_signal (tid, SIGURG);
201	sleep (1);
202	send_process_signal (SIGURG);
203	sleep (1);
204	if (sigcounts[SIGURG] != 2)
205		fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGURG.\n");
206
207	/*
208	 * Verify that a signal with a default action that terminates
209	 * the process will release a sigwait.
210	 */
211	send_thread_signal (tid, SIGUSR1);
212	sleep (1);
213	send_process_signal (SIGUSR1);
214	sleep (1);
215	if (sigcounts[SIGUSR1] != 2)
216		fprintf (stderr,
217		    "FAIL: sigwait doesn't wake up for SIGUSR1.\n");
218
219	/*
220	 * Verify that if we install a signal handler for a previously
221	 * ignored signal, an occurrence of this signal will release
222	 * the (already waiting) sigwait.
223	 */
224
225	/* Install a signal handler for SIGHUP. */
226	sigemptyset (&act.sa_mask);
227	sigaddset (&act.sa_mask, SIGHUP);
228	act.sa_handler = sighandler;
229	act.sa_flags = SA_RESTART;
230	sigaction (SIGHUP, &act, NULL);
231
232	/* Sending SIGHUP should release the sigwait. */
233	send_process_signal (SIGHUP);
234	sleep (1);
235	send_thread_signal (tid, SIGHUP);
236	sleep (1);
237	if (sigcounts[SIGHUP] != 2)
238		fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGHUP.\n");
239
240	/*
241	 * Verify that a pending signal in the waiters mask will
242	 * cause sigwait to return the pending signal.  We do this
243	 * by taking the waiters mutex and signaling the waiter to
244	 * release him from the sigwait.  The waiter will block
245	 * on taking the mutex, and we can then send the waiter a
246	 * signal which should be added to his pending signals.
247	 * The next time the waiter does a sigwait, he should
248	 * return with the pending signal.
249	 */
250	sigcounts[SIGHUP] = 0;
251 	pthread_mutex_lock (&waiter_mutex);
252	/* Release the waiter from sigwait. */
253	send_process_signal (SIGHUP);
254	sleep (1);
255	if (sigcounts[SIGHUP] != 1)
256		fprintf (stderr, "FAIL: sigwait doesn't wake up for SIGHUP.\n");
257	/*
258	 * Add SIGHUP to the process pending signals.  Since there is
259	 * a signal handler installed for SIGHUP and this signal is
260	 * blocked from the waiter thread and unblocked in the main
261	 * thread, the signal handler should be called once for SIGHUP.
262	 */
263	send_process_signal (SIGHUP);
264	/* Release the waiter thread and allow him to run. */
265	pthread_mutex_unlock (&waiter_mutex);
266	sleep (1);
267	if (sigcounts[SIGHUP] != 2)
268		fprintf (stderr,
269		    "FAIL: sigwait doesn't return for pending SIGHUP.\n");
270
271	/*
272	 * Repeat the above test using pthread_kill and SIGUSR1.
273	 */
274	sigcounts[SIGUSR1] = 0;
275 	pthread_mutex_lock (&waiter_mutex);
276	/* Release the waiter from sigwait. */
277	send_thread_signal (tid, SIGUSR1);
278	sleep (1);
279	if (sigcounts[SIGUSR1] != 1)
280		fprintf (stderr,
281		    "FAIL: sigwait doesn't wake up for SIGUSR1.\n");
282	/* Add SIGUSR1 to the waiters pending signals. */
283	send_thread_signal (tid, SIGUSR1);
284	/* Release the waiter thread and allow him to run. */
285	pthread_mutex_unlock (&waiter_mutex);
286	sleep (1);
287	if (sigcounts[SIGUSR1] != 2)
288		fprintf (stderr,
289		    "FAIL: sigwait doesn't return for pending SIGUSR1.\n");
290
291	/*
292	 * Verify that we can still kill the process for a signal
293	 * not being waited on by sigwait.
294	 */
295	send_process_signal (SIGPIPE);
296	fprintf (stderr, "FAIL: SIGPIPE did not terminate process.\n");
297
298	/*
299	 * Wait for the thread to finish.
300	 */
301	pthread_join (tid, &exit_status);
302
303	return (0);
304}
305