159612Sjasone/*
259612Sjasone * Copyright (c) 1998 Daniel M. Eischen <eischen@vigrid.com>
359612Sjasone * All rights reserved.
459612Sjasone *
559612Sjasone * Redistribution and use in source and binary forms, with or without
659612Sjasone * modification, are permitted provided that the following conditions
759612Sjasone * are met:
859612Sjasone * 1. Redistributions of source code must retain the above copyright
959612Sjasone *    notice, this list of conditions and the following disclaimer.
1059612Sjasone * 2. Redistributions in binary form must reproduce the above copyright
1159612Sjasone *    notice, this list of conditions and the following disclaimer in the
1259612Sjasone *    documentation and/or other materials provided with the distribution.
1359612Sjasone * 3. All advertising materials mentioning features or use of this software
1459612Sjasone *    must display the following acknowledgement:
1559612Sjasone *	This product includes software developed by Daniel M. Eischen.
1659612Sjasone * 4. Neither the name of the author nor the names of any co-contributors
1759612Sjasone *    may be used to endorse or promote products derived from this software
1859612Sjasone *    without specific prior written permission.
1959612Sjasone *
2059612Sjasone * THIS SOFTWARE IS PROVIDED BY DANIEL M. EISCHEN AND CONTRIBUTORS ``AS IS''
2159612Sjasone * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259612Sjasone * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359612Sjasone * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459612Sjasone * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559612Sjasone * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659612Sjasone * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759612Sjasone * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859612Sjasone * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959612Sjasone * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059612Sjasone * SUCH DAMAGE.
3159612Sjasone *
3259612Sjasone * $FreeBSD$
3359612Sjasone */
3459612Sjasone#include <stdlib.h>
3559612Sjasone#include <unistd.h>
3659612Sjasone
3759612Sjasone#include <errno.h>
3859612Sjasone#include <pthread.h>
3959612Sjasone#include <signal.h>
4059612Sjasone#include <stdio.h>
4159612Sjasone#include <string.h>
4259612Sjasone
4359612Sjasone#if defined(_LIBC_R_)
4459612Sjasone#include <pthread_np.h>
4559612Sjasone#endif
4659612Sjasone
4759612Sjasonestatic int	sigcounts[NSIG + 1];
4859612Sjasonestatic int	sigfifo[NSIG + 1];
4959612Sjasonestatic int	fifo_depth = 0;
5059612Sjasonestatic sigset_t suspender_mask;
5159612Sjasonestatic pthread_t suspender_tid;
5259612Sjasone
5359612Sjasone
5459612Sjasonestatic void *
5559612Sjasonesigsuspender (void *arg)
5659612Sjasone{
5759612Sjasone	int save_count, status, i;
5859612Sjasone	sigset_t run_mask;
5959612Sjasone
6059612Sjasone	/* Run with all signals blocked. */
6159612Sjasone	sigfillset (&run_mask);
6259612Sjasone	sigprocmask (SIG_SETMASK, &run_mask, NULL);
6359612Sjasone
6459612Sjasone	/* Allow these signals to wake us up during a sigsuspend. */
6559612Sjasone	sigfillset (&suspender_mask);		/* Default action	*/
66117706Sdavidxu	sigdelset (&suspender_mask, SIGKILL);	/* Cannot catch		*/
67117706Sdavidxu	sigdelset (&suspender_mask, SIGSTOP);	/* Cannot catch		*/
6859612Sjasone	sigdelset (&suspender_mask, SIGINT);	/* terminate		*/
6959612Sjasone	sigdelset (&suspender_mask, SIGHUP);	/* terminate		*/
7059612Sjasone	sigdelset (&suspender_mask, SIGQUIT);	/* create core image	*/
7159612Sjasone	sigdelset (&suspender_mask, SIGURG);	/* ignore		*/
7259612Sjasone	sigdelset (&suspender_mask, SIGIO);	/* ignore		*/
7359612Sjasone	sigdelset (&suspender_mask, SIGUSR2);	/* terminate		*/
7459612Sjasone
7559612Sjasone	while (sigcounts[SIGINT] == 0) {
7659612Sjasone		save_count = sigcounts[SIGUSR2];
7759612Sjasone
7859612Sjasone		status = sigsuspend (&suspender_mask);
7959612Sjasone		if ((status == 0) || (errno != EINTR)) {
8059612Sjasone			fprintf (stderr, "Unable to suspend for signals, "
8159612Sjasone				"errno %d, return value %d\n",
8259612Sjasone				errno, status);
8359612Sjasone			exit (1);
8459612Sjasone		}
8559612Sjasone		for (i = 0; i < fifo_depth; i++)
8659612Sjasone			fprintf (stderr, "Sigsuspend woke up by signal %d\n",
8759612Sjasone				sigfifo[i]);
8859612Sjasone		fifo_depth = 0;
8959612Sjasone	}
9059612Sjasone
9159612Sjasone	pthread_exit (arg);
9259612Sjasone	return (NULL);
9359612Sjasone}
9459612Sjasone
9559612Sjasone
9659612Sjasonestatic void
9759612Sjasonesighandler (int signo)
9859612Sjasone{
9959612Sjasone	sigset_t set, suspend_set;
10059612Sjasone	pthread_t self;
10159612Sjasone
10259612Sjasone	if ((signo >= 0) && (signo <= NSIG))
10359612Sjasone		sigcounts[signo]++;
10459612Sjasone
10559612Sjasone	/*
10659612Sjasone	 * If we are running on behalf of the suspender thread,
10759612Sjasone	 * ensure that we have the correct mask set.
10859612Sjasone	 */
10959612Sjasone	self = pthread_self ();
11059612Sjasone	if (self == suspender_tid) {
11159612Sjasone		sigfifo[fifo_depth] = signo;
11259612Sjasone		fifo_depth++;
11359612Sjasone		fprintf (stderr,
11459612Sjasone		    "  -> Suspender thread signal handler caught signal %d\n",
11559612Sjasone		    signo);
11659612Sjasone
11759612Sjasone		/* Get the current signal mask. */
11859612Sjasone		sigprocmask (SIG_SETMASK, NULL, &set);
11959612Sjasone
12059612Sjasone		/* The handler should run with the current signal masked. */
12159612Sjasone		suspend_set = suspender_mask;
12259612Sjasone		sigaddset(&suspend_set, signo);
12359612Sjasone
12459612Sjasone		if (memcmp(&set, &suspend_set, sizeof(set)))
12559612Sjasone			fprintf (stderr,
12659612Sjasone			    "  >>> FAIL: sigsuspender signal handler running "
12759612Sjasone			    "with incorrect mask.\n");
12859612Sjasone	}
12959612Sjasone	else
13059612Sjasone		fprintf (stderr,
13159612Sjasone		    "  -> Main thread signal handler caught signal %d\n",
13259612Sjasone		    signo);
13359612Sjasone}
13459612Sjasone
13559612Sjasone
13659612Sjasonestatic void
13759612Sjasonesend_thread_signal (pthread_t tid, int signo)
13859612Sjasone{
13959612Sjasone	if (pthread_kill (tid, signo) != 0) {
14059612Sjasone		fprintf (stderr, "Unable to send thread signal, errno %d.\n",
14159612Sjasone		    errno);
14259612Sjasone		exit (1);
14359612Sjasone	}
14459612Sjasone}
14559612Sjasone
14659612Sjasone
14759612Sjasonestatic void
14859612Sjasonesend_process_signal (int signo)
14959612Sjasone{
15059612Sjasone	if (kill (getpid (), signo) != 0) {
15159612Sjasone		fprintf (stderr, "Unable to send process signal, errno %d.\n",
15259612Sjasone		    errno);
15359612Sjasone		exit (1);
15459612Sjasone	}
15559612Sjasone}
15659612Sjasone
15759612Sjasone
15859612Sjasoneint main (int argc, char *argv[])
15959612Sjasone{
16059612Sjasone	pthread_attr_t	pattr;
16159612Sjasone	void *		exit_status;
16259612Sjasone	struct sigaction act;
16359612Sjasone	sigset_t	oldset;
16459612Sjasone	sigset_t	newset;
16559612Sjasone
16659612Sjasone	/* Initialize our signal counts. */
16759612Sjasone	memset ((void *) sigcounts, 0, NSIG * sizeof (int));
16859612Sjasone
16959612Sjasone	/* Ignore signal SIGIO. */
17059612Sjasone	sigemptyset (&act.sa_mask);
17159612Sjasone	sigaddset (&act.sa_mask, SIGIO);
17259612Sjasone	act.sa_handler = SIG_IGN;
17359612Sjasone	act.sa_flags = 0;
17459612Sjasone	sigaction (SIGIO, &act, NULL);
17559612Sjasone
17659612Sjasone	/* Install a signal handler for SIGURG. */
17759612Sjasone	sigemptyset (&act.sa_mask);
17859612Sjasone	sigaddset (&act.sa_mask, SIGURG);
17959612Sjasone	act.sa_handler = sighandler;
18059612Sjasone	act.sa_flags = SA_RESTART;
18159612Sjasone	sigaction (SIGURG, &act, NULL);
18259612Sjasone
18359612Sjasone	/* Install a signal handler for SIGXCPU */
18459612Sjasone	sigemptyset (&act.sa_mask);
18559612Sjasone	sigaddset (&act.sa_mask, SIGXCPU);
18659612Sjasone	sigaction (SIGXCPU, &act, NULL);
18759612Sjasone
18859612Sjasone	/* Get our current signal mask. */
18959612Sjasone	sigprocmask (SIG_SETMASK, NULL, &oldset);
19059612Sjasone
19159612Sjasone	/* Mask out SIGUSR1 and SIGUSR2. */
19259612Sjasone	newset = oldset;
19359612Sjasone	sigaddset (&newset, SIGUSR1);
19459612Sjasone	sigaddset (&newset, SIGUSR2);
19559612Sjasone	sigprocmask (SIG_SETMASK, &newset, NULL);
19659612Sjasone
19759612Sjasone	/* Install a signal handler for SIGUSR1 */
19859612Sjasone	sigemptyset (&act.sa_mask);
19959612Sjasone	sigaddset (&act.sa_mask, SIGUSR1);
20059612Sjasone	sigaction (SIGUSR1, &act, NULL);
20159612Sjasone
20259612Sjasone	/* Install a signal handler for SIGUSR2 */
20359612Sjasone	sigemptyset (&act.sa_mask);
20459612Sjasone	sigaddset (&act.sa_mask, SIGUSR2);
20559612Sjasone	sigaction (SIGUSR2, &act, NULL);
20659612Sjasone
20759612Sjasone	/*
20859612Sjasone	 * Initialize the thread attribute.
20959612Sjasone	 */
21059612Sjasone	if ((pthread_attr_init (&pattr) != 0) ||
21159612Sjasone	    (pthread_attr_setdetachstate (&pattr,
21259612Sjasone	    PTHREAD_CREATE_JOINABLE) != 0)) {
21359612Sjasone		fprintf (stderr, "Unable to initialize thread attributes.\n");
21459612Sjasone		exit (1);
21559612Sjasone	}
21659612Sjasone
21759612Sjasone	/*
21859612Sjasone	 * Create the sigsuspender thread.
21959612Sjasone	 */
22059612Sjasone	if (pthread_create (&suspender_tid, &pattr, sigsuspender, NULL) != 0) {
22159612Sjasone		fprintf (stderr, "Unable to create thread, errno %d.\n", errno);
22259612Sjasone		exit (1);
22359612Sjasone	}
224152597Smarcel#if defined(_LIBC_R_)
22559612Sjasone	pthread_set_name_np (suspender_tid, "sigsuspender");
22659612Sjasone#endif
22759612Sjasone
22859612Sjasone	/*
22959612Sjasone	 * Verify that an ignored signal doesn't cause a wakeup.
23059612Sjasone	 * We don't have a handler installed for SIGIO.
23159612Sjasone	 */
23259612Sjasone	send_thread_signal (suspender_tid, SIGIO);
23359612Sjasone	sleep (1);
23459612Sjasone	send_process_signal (SIGIO);
23559612Sjasone	sleep (1);
23659612Sjasone	if (sigcounts[SIGIO] != 0)
23759612Sjasone		fprintf (stderr, "FAIL: sigsuspend wakes up for ignored signal "
23859612Sjasone			"SIGIO.\n");
23959612Sjasone
24059612Sjasone	/*
24159612Sjasone	 * Verify that a signal with a default action of ignore, for
24259612Sjasone	 * which we have a signal handler installed, will release a
24359612Sjasone	 * sigsuspend.
24459612Sjasone	 */
24559612Sjasone	send_thread_signal (suspender_tid, SIGURG);
24659612Sjasone	sleep (1);
24759612Sjasone	send_process_signal (SIGURG);
24859612Sjasone	sleep (1);
24959612Sjasone	if (sigcounts[SIGURG] != 2)
25059612Sjasone		fprintf (stderr,
25159612Sjasone		    "FAIL: sigsuspend doesn't wake up for SIGURG.\n");
25259612Sjasone
25359612Sjasone	/*
25459612Sjasone	 * Verify that a SIGUSR2 signal will release a sigsuspended
25559612Sjasone	 * thread.
25659612Sjasone	 */
25759612Sjasone	send_thread_signal (suspender_tid, SIGUSR2);
25859612Sjasone	sleep (1);
25959612Sjasone	send_process_signal (SIGUSR2);
26059612Sjasone	sleep (1);
26159612Sjasone	if (sigcounts[SIGUSR2] != 2)
26259612Sjasone		fprintf (stderr,
26359612Sjasone		    "FAIL: sigsuspend doesn't wake up for SIGUSR2.\n");
26459612Sjasone
26559612Sjasone	/*
26659612Sjasone	 * Verify that a signal, blocked in both the main and
26759612Sjasone	 * sigsuspender threads, does not cause the signal handler
26859612Sjasone	 * to be called.
26959612Sjasone	 */
27059612Sjasone	send_thread_signal (suspender_tid, SIGUSR1);
27159612Sjasone	sleep (1);
27259612Sjasone	send_process_signal (SIGUSR1);
27359612Sjasone	sleep (1);
27459612Sjasone	if (sigcounts[SIGUSR1] != 0)
27559612Sjasone		fprintf (stderr, "FAIL: signal hander called for SIGUSR1.\n");
27659612Sjasone
27759612Sjasone	/*
27859612Sjasone	 * Verify that we can still kill the process for a signal
27959612Sjasone	 * not being waited on by sigwait.
28059612Sjasone	 */
28159612Sjasone	send_process_signal (SIGPIPE);
28259612Sjasone	fprintf (stderr, "FAIL: SIGPIPE did not terminate process.\n");
28359612Sjasone
28459612Sjasone	/*
28559612Sjasone	 * Wait for the thread to finish.
28659612Sjasone	 */
28759612Sjasone	pthread_join (suspender_tid, &exit_status);
28859612Sjasone
28959612Sjasone	return (0);
29059612Sjasone}
291