The implementation of sigwait(s) assumed that all signals in s have
[kopensolaris-gnu/glibc.git] / linuxthreads / signals.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program is distributed in the hope that it will be useful,      */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
13 /* GNU Library General Public License for more details.                 */
14
15 /* Handling of signals */
16
17 #include <errno.h>
18 #include <signal.h>
19 #include "pthread.h"
20 #include "internals.h"
21 #include "spinlock.h"
22
23 int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
24 {
25   sigset_t mask;
26
27   if (newmask != NULL) {
28     mask = *newmask;
29     /* Don't allow __pthread_sig_restart to be unmasked.
30        Don't allow __pthread_sig_cancel to be masked. */
31     switch(how) {
32     case SIG_SETMASK:
33       sigaddset(&mask, __pthread_sig_restart);
34       sigdelset(&mask, __pthread_sig_cancel);
35       break;
36     case SIG_BLOCK:
37       sigdelset(&mask, __pthread_sig_cancel);
38       break;
39     case SIG_UNBLOCK:
40       sigdelset(&mask, __pthread_sig_restart);
41       break;
42     }
43     newmask = &mask;
44   }
45   if (sigprocmask(how, newmask, oldmask) == -1)
46     return errno;
47   else
48     return 0;
49 }
50
51 int pthread_kill(pthread_t thread, int signo)
52 {
53   pthread_handle handle = thread_handle(thread);
54   int pid;
55
56   __pthread_lock(&handle->h_lock, NULL);
57   if (invalid_handle(handle, thread)) {
58     __pthread_unlock(&handle->h_lock);
59     return ESRCH;
60   }
61   pid = handle->h_descr->p_pid;
62   __pthread_unlock(&handle->h_lock);
63   if (kill(pid, signo) == -1)
64     return errno;
65   else
66     return 0;
67 }
68
69 /* User-provided signal handlers */
70 static __sighandler_t sighandler[NSIG];
71
72 /* The wrapper around user-provided signal handlers */
73 static void pthread_sighandler(int signo)
74 {
75   pthread_descr self = thread_self();
76   char * in_sighandler;
77   /* If we're in a sigwait operation, just record the signal received
78      and return without calling the user's handler */
79   if (THREAD_GETMEM(self, p_sigwaiting)) {
80     THREAD_SETMEM(self, p_sigwaiting, 0);
81     THREAD_SETMEM(self, p_signal, signo);
82     return;
83   }
84   /* Record that we're in a signal handler and call the user's
85      handler function */
86   in_sighandler = THREAD_GETMEM(self, p_in_sighandler);
87   if (in_sighandler == NULL)
88     THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME);
89   sighandler[signo](signo);
90   if (in_sighandler == NULL)
91     THREAD_SETMEM(self, p_in_sighandler, NULL);
92 }
93
94 /* The wrapper around sigaction.  Install our own signal handler
95    around the signal. */
96 int sigaction(int sig, const struct sigaction * act,
97               struct sigaction * oact)
98 {
99   struct sigaction newact;
100   struct sigaction *newactp;
101
102   if (sig == __pthread_sig_restart ||
103       sig == __pthread_sig_cancel ||
104       (sig == __pthread_sig_debug && __pthread_sig_debug > 0))
105     return EINVAL;
106   if (act)
107     {
108       newact = *act;
109       if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL
110           && sig > 0 && sig < NSIG)
111         newact.sa_handler = pthread_sighandler;
112       newactp = &newact;
113     }
114   else
115     newactp = NULL;
116   if (__sigaction(sig, newactp, oact) == -1)
117     return -1;
118   if (sig > 0 && sig < NSIG)
119     {
120       if (oact != NULL)
121         oact->sa_handler = sighandler[sig];
122       if (act)
123         sighandler[sig] = act->sa_handler;
124     }
125   return 0;
126 }
127
128 /* A signal handler that does nothing */
129 static void pthread_null_sighandler(int sig) { }
130
131 /* sigwait -- synchronously wait for a signal */
132 int sigwait(const sigset_t * set, int * sig)
133 {
134   volatile pthread_descr self = thread_self();
135   sigset_t mask;
136   int s;
137   sigjmp_buf jmpbuf;
138   struct sigaction sa;
139
140   /* Get ready to block all signals except those in set
141      and the cancellation signal.
142      Also check that handlers are installed on all signals in set,
143      and if not, install our dummy handler.  This is conformant to
144      POSIX: "The effect of sigwait() on the signal actions for the
145      signals in set is unspecified." */
146   sigfillset(&mask);
147   sigdelset(&mask, __pthread_sig_cancel);
148   for (s = 1; s <= NSIG; s++) {
149     if (sigismember(set, s) &&
150         s != __pthread_sig_restart &&
151         s != __pthread_sig_cancel &&
152         s != __pthread_sig_debug) {
153       sigdelset(&mask, s);
154       if (sighandler[s] == NULL ||
155           sighandler[s] == SIG_DFL ||
156           sighandler[s] == SIG_IGN) {
157         sa.sa_handler = pthread_null_sighandler;
158         sigemptyset(&sa.sa_mask);
159         sa.sa_flags = 0;
160         sigaction(s, &sa, NULL);
161       }
162     }
163   }
164   /* Test for cancellation */
165   if (sigsetjmp(jmpbuf, 1) == 0) {
166     THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf);
167     if (! (THREAD_GETMEM(self, p_canceled)
168            && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) {
169       /* Reset the signal count */
170       THREAD_SETMEM(self, p_signal, 0);
171       /* Say we're in sigwait */
172       THREAD_SETMEM(self, p_sigwaiting, 1);
173       /* Unblock the signals and wait for them */
174       sigsuspend(&mask);
175     }
176   }
177   THREAD_SETMEM(self, p_cancel_jmp, NULL);
178   /* The signals are now reblocked.  Check for cancellation */
179   pthread_testcancel();
180   /* We should have self->p_signal != 0 and equal to the signal received */
181   *sig = THREAD_GETMEM(self, p_signal);
182   return 0;
183 }
184
185 /* Redefine raise() to send signal to calling thread only,
186    as per POSIX 1003.1c */
187 int raise (int sig)
188 {
189   int retcode = pthread_kill(pthread_self(), sig);
190   if (retcode == 0)
191     return 0;
192   else {
193     errno = retcode;
194     return -1;
195   }
196 }