1 /* $OpenBSD: sigwait.c,v 1.7 2017/05/27 14:24:28 mpi 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 sigset_t wait_mask; 48 static pthread_mutex_t waiter_mutex; 49 50 51 static void * 52 sigwaiter (void *arg) 53 { 54 int signo; 55 56 SET_NAME("sigwaiter"); 57 58 while (sigcounts[SIGINT] == 0) { 59 printf("Sigwait waiting (thread %p)\n", pthread_self()); 60 CHECKe(sigwait (&wait_mask, &signo)); 61 sigcounts[signo]++; 62 printf ("Sigwait caught signal %d (%s)\n", signo, 63 strsignal(signo)); 64 65 /* Allow the main thread to prevent the sigwait. */ 66 CHECKr(pthread_mutex_lock (&waiter_mutex)); 67 CHECKr(pthread_mutex_unlock (&waiter_mutex)); 68 } 69 70 return (arg); 71 } 72 73 74 static void 75 sighandler (int signo) 76 { 77 int save_errno = errno; 78 char buf[8192]; 79 80 snprintf(buf, sizeof buf, 81 " -> Signal handler caught signal %d (%s) in thread %p\n", 82 signo, strsignal(signo), pthread_self()); 83 write(STDOUT_FILENO, buf, strlen(buf)); 84 85 if ((signo >= 0) && (signo <= NSIG)) 86 sigcounts[signo]++; 87 errno = save_errno; 88 } 89 90 int main (int argc, char *argv[]) 91 { 92 pthread_mutexattr_t mattr; 93 pthread_attr_t pattr; 94 pthread_t tid; 95 struct sigaction act; 96 97 /* Initialize our signal counts. */ 98 memset ((void *) sigcounts, 0, NSIG * sizeof (int)); 99 100 /* Setup our wait mask. */ 101 sigemptyset (&wait_mask); /* Default action */ 102 sigaddset (&wait_mask, SIGHUP); /* terminate */ 103 sigaddset (&wait_mask, SIGINT); /* terminate */ 104 sigaddset (&wait_mask, SIGQUIT); /* create core image */ 105 sigaddset (&wait_mask, SIGURG); /* ignore */ 106 sigaddset (&wait_mask, SIGIO); /* ignore */ 107 sigaddset (&wait_mask, SIGUSR1); /* terminate */ 108 109 /* Block all of the signals that will be waited for */ 110 CHECKe(sigprocmask (SIG_BLOCK, &wait_mask, NULL)); 111 112 /* Ignore signals SIGHUP and SIGIO. */ 113 sigemptyset (&act.sa_mask); 114 sigaddset (&act.sa_mask, SIGHUP); 115 sigaddset (&act.sa_mask, SIGIO); 116 act.sa_handler = SIG_IGN; 117 act.sa_flags = 0; 118 CHECKe(sigaction (SIGHUP, &act, NULL)); 119 CHECKe(sigaction (SIGIO, &act, NULL)); 120 121 /* Install a signal handler for SIGURG */ 122 sigemptyset (&act.sa_mask); 123 sigaddset (&act.sa_mask, SIGURG); 124 act.sa_handler = sighandler; 125 act.sa_flags = SA_RESTART; 126 CHECKe(sigaction (SIGURG, &act, NULL)); 127 128 /* Install a signal handler for SIGXCPU */ 129 sigemptyset (&act.sa_mask); 130 sigaddset (&act.sa_mask, SIGXCPU); 131 CHECKe(sigaction (SIGXCPU, &act, NULL)); 132 133 /* 134 * Initialize the thread attribute. 135 */ 136 CHECKr(pthread_attr_init (&pattr)); 137 CHECKr(pthread_attr_setdetachstate (&pattr, PTHREAD_CREATE_JOINABLE)); 138 139 /* 140 * Initialize and create a mutex. 141 */ 142 CHECKr(pthread_mutexattr_init (&mattr)); 143 CHECKr(pthread_mutex_init (&waiter_mutex, &mattr)); 144 145 /* 146 * Create the sigwaiter thread. 147 */ 148 CHECKr(pthread_create (&tid, &pattr, sigwaiter, NULL)); 149 150 #if 0 /* XXX To quote POSIX 2008, XSH, from section 2.4.1 151 * (Signal Generation and Delivery) paragraph 4: 152 * If the action associated with a blocked signal is to 153 * ignore the signal and if that signal is generated for 154 * the process, it is unspecified whether the signal is 155 * discarded immediately upon generation or remains pending. 156 * So, SIGIO may remain pending here and be accepted by the sigwait() 157 * in the other thread, even though its disposition is "ignored". 158 */ 159 /* 160 * Verify that an ignored signal doesn't cause a wakeup. 161 * We don't have a handler installed for SIGIO. 162 */ 163 CHECKr(pthread_kill (tid, SIGIO)); 164 sleep (1); 165 CHECKe(kill(getpid(), SIGIO)); 166 sleep (1); 167 /* sigwait should not wake up for ignored signal SIGIO */ 168 ASSERT(sigcounts[SIGIO] == 0); 169 #endif 170 171 /* 172 * Verify that a signal with a default action of ignore, for 173 * which we have a signal handler installed, will release a sigwait. 174 */ 175 CHECKr(pthread_kill (tid, SIGURG)); 176 sleep (1); 177 CHECKe(kill(getpid(), SIGURG)); 178 sleep (1); 179 /* sigwait should wake up for SIGURG */ 180 ASSERT(sigcounts[SIGURG] == 2); 181 182 /* 183 * Verify that a signal with a default action that terminates 184 * the process will release a sigwait. 185 */ 186 CHECKr(pthread_kill (tid, SIGUSR1)); 187 sleep (1); 188 CHECKe(kill(getpid(), SIGUSR1)); 189 sleep (1); 190 if (sigcounts[SIGUSR1] != 2) 191 printf ("FAIL: sigwait doesn't wake up for SIGUSR1.\n"); 192 193 /* 194 * Verify that if we install a signal handler for a previously 195 * ignored signal, an occurrence of this signal will release 196 * the (already waiting) sigwait. 197 */ 198 199 /* Install a signal handler for SIGHUP. */ 200 sigemptyset (&act.sa_mask); 201 sigaddset (&act.sa_mask, SIGHUP); 202 act.sa_handler = sighandler; 203 act.sa_flags = SA_RESTART; 204 CHECKe(sigaction (SIGHUP, &act, NULL)); 205 206 /* Sending SIGHUP should release the sigwait. */ 207 CHECKe(kill(getpid(), SIGHUP)); 208 sleep (1); 209 CHECKr(pthread_kill (tid, SIGHUP)); 210 sleep (1); 211 /* sigwait should wake up for SIGHUP */ 212 ASSERT(sigcounts[SIGHUP] == 2); 213 214 /* 215 * Verify that a pending signal in the waiters mask will 216 * cause sigwait to return the pending signal. We do this 217 * by taking the waiters mutex and signaling the waiter to 218 * release him from the sigwait. The waiter will block 219 * on taking the mutex, and we can then send the waiter a 220 * signal which should be added to his pending signals. 221 * The next time the waiter does a sigwait, he should 222 * return with the pending signal. 223 */ 224 sigcounts[SIGHUP] = 0; 225 CHECKr(pthread_mutex_lock (&waiter_mutex)); 226 /* Release the waiter from sigwait. */ 227 CHECKe(kill(getpid(), SIGHUP)); 228 sleep (1); 229 /* signal waiter should wake up for SIGHUP */ 230 ASSERT(sigcounts[SIGHUP] == 1); 231 /* Release the waiter thread and allow him to run. */ 232 CHECKr(pthread_mutex_unlock (&waiter_mutex)); 233 sleep (1); 234 235 /* 236 * Repeat the above test using pthread_kill and SIGUSR1 237 */ 238 sigcounts[SIGUSR1] = 0; 239 CHECKr(pthread_mutex_lock (&waiter_mutex)); 240 /* Release the waiter from sigwait. */ 241 CHECKr(pthread_kill (tid, SIGUSR1)); 242 sleep (1); 243 /* sigwait should wake up for SIGUSR1 */ 244 ASSERT(sigcounts[SIGUSR1] == 1); 245 /* Add SIGUSR1 to the waiters pending signals. */ 246 CHECKr(pthread_kill (tid, SIGUSR1)); 247 /* Release the waiter thread and allow him to run. */ 248 CHECKr(pthread_mutex_unlock (&waiter_mutex)); 249 sleep (1); 250 /* sigwait should return for pending SIGUSR1 */ 251 ASSERT(sigcounts[SIGUSR1] == 2); 252 253 #if 0 254 /* 255 * Verify that we can still kill the process for a signal 256 * not being waited on by sigwait. 257 */ 258 CHECKe(kill(getpid(), SIGPIPE)); 259 PANIC("SIGPIPE did not terminate process"); 260 261 /* 262 * Wait for the thread to finish. 263 */ 264 CHECKr(pthread_join (tid, NULL)); 265 #endif 266 267 SUCCEED; 268 } 269