Pass sigcontext through wrapper to the user function.
[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 #include <sigcontextinfo.h>
23
24 int pthread_sigmask(int how, const sigset_t * newmask, sigset_t * oldmask)
25 {
26   sigset_t mask;
27
28   if (newmask != NULL) {
29     mask = *newmask;
30     /* Don't allow __pthread_sig_restart to be unmasked.
31        Don't allow __pthread_sig_cancel to be masked. */
32     switch(how) {
33     case SIG_SETMASK:
34       sigaddset(&mask, __pthread_sig_restart);
35       sigdelset(&mask, __pthread_sig_cancel);
36       break;
37     case SIG_BLOCK:
38       sigdelset(&mask, __pthread_sig_cancel);
39       break;
40     case SIG_UNBLOCK:
41       sigdelset(&mask, __pthread_sig_restart);
42       break;
43     }
44     newmask = &mask;
45   }
46   if (sigprocmask(how, newmask, oldmask) == -1)
47     return errno;
48   else
49     return 0;
50 }
51
52 int pthread_kill(pthread_t thread, int signo)
53 {
54   pthread_handle handle = thread_handle(thread);
55   int pid;
56
57   __pthread_lock(&handle->h_lock, NULL);
58   if (invalid_handle(handle, thread)) {
59     __pthread_unlock(&handle->h_lock);
60     return ESRCH;
61   }
62   pid = handle->h_descr->p_pid;
63   __pthread_unlock(&handle->h_lock);
64   if (kill(pid, signo) == -1)
65     return errno;
66   else
67     return 0;
68 }
69
70 /* User-provided signal handlers */
71 typedef void (*arch_sighandler_t) __PMT ((int, SIGCONTEXT));
72 static arch_sighandler_t sighandler[NSIG];
73
74 /* The wrapper around user-provided signal handlers */
75 static void pthread_sighandler(int signo, SIGCONTEXT ctx)
76 {
77   pthread_descr self = thread_self();
78   char * in_sighandler;
79   /* If we're in a sigwait operation, just record the signal received
80      and return without calling the user's handler */
81   if (THREAD_GETMEM(self, p_sigwaiting)) {
82     THREAD_SETMEM(self, p_sigwaiting, 0);
83     THREAD_SETMEM(self, p_signal, signo);
84     return;
85   }
86   /* Record that we're in a signal handler and call the user's
87      handler function */
88   in_sighandler = THREAD_GETMEM(self, p_in_sighandler);
89   if (in_sighandler == NULL)
90     THREAD_SETMEM(self, p_in_sighandler, CURRENT_STACK_FRAME);
91   sighandler[signo](signo, ctx);
92   if (in_sighandler == NULL)
93     THREAD_SETMEM(self, p_in_sighandler, NULL);
94 }
95
96 /* The wrapper around sigaction.  Install our own signal handler
97    around the signal. */
98 int sigaction(int sig, const struct sigaction * act,
99               struct sigaction * oact)
100 {
101   struct sigaction newact;
102   struct sigaction *newactp;
103
104   if (sig == __pthread_sig_restart ||
105       sig == __pthread_sig_cancel ||
106       (sig == __pthread_sig_debug && __pthread_sig_debug > 0))
107     return EINVAL;
108   if (act)
109     {
110       newact = *act;
111       if (act->sa_handler != SIG_IGN && act->sa_handler != SIG_DFL
112           && sig > 0 && sig < NSIG)
113         newact.sa_handler = (__sighandler_t) pthread_sighandler;
114       newactp = &newact;
115     }
116   else
117     newactp = NULL;
118   if (__sigaction(sig, newactp, oact) == -1)
119     return -1;
120   if (sig > 0 && sig < NSIG)
121     {
122       if (oact != NULL)
123         oact->sa_handler = (__sighandler_t) sighandler[sig];
124       if (act)
125         sighandler[sig] = (arch_sighandler_t) act->sa_handler;
126     }
127   return 0;
128 }
129
130 /* A signal handler that does nothing */
131 static void pthread_null_sighandler(int sig) { }
132
133 /* sigwait -- synchronously wait for a signal */
134 int sigwait(const sigset_t * set, int * sig)
135 {
136   volatile pthread_descr self = thread_self();
137   sigset_t mask;
138   int s;
139   sigjmp_buf jmpbuf;
140   struct sigaction sa;
141
142   /* Get ready to block all signals except those in set
143      and the cancellation signal.
144      Also check that handlers are installed on all signals in set,
145      and if not, install our dummy handler.  This is conformant to
146      POSIX: "The effect of sigwait() on the signal actions for the
147      signals in set is unspecified." */
148   sigfillset(&mask);
149   sigdelset(&mask, __pthread_sig_cancel);
150   for (s = 1; s <= NSIG; s++) {
151     if (sigismember(set, s) &&
152         s != __pthread_sig_restart &&
153         s != __pthread_sig_cancel &&
154         s != __pthread_sig_debug) {
155       sigdelset(&mask, s);
156       if (sighandler[s] == NULL ||
157           sighandler[s] == (arch_sighandler_t) SIG_DFL ||
158           sighandler[s] == (arch_sighandler_t) SIG_IGN) {
159         sa.sa_handler = pthread_null_sighandler;
160         sigemptyset(&sa.sa_mask);
161         sa.sa_flags = 0;
162         sigaction(s, &sa, NULL);
163       }
164     }
165   }
166   /* Test for cancellation */
167   if (sigsetjmp(jmpbuf, 1) == 0) {
168     THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf);
169     if (! (THREAD_GETMEM(self, p_canceled)
170            && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) {
171       /* Reset the signal count */
172       THREAD_SETMEM(self, p_signal, 0);
173       /* Say we're in sigwait */
174       THREAD_SETMEM(self, p_sigwaiting, 1);
175       /* Unblock the signals and wait for them */
176       sigsuspend(&mask);
177     }
178   }
179   THREAD_SETMEM(self, p_cancel_jmp, NULL);
180   /* The signals are now reblocked.  Check for cancellation */
181   pthread_testcancel();
182   /* We should have self->p_signal != 0 and equal to the signal received */
183   *sig = THREAD_GETMEM(self, p_signal);
184   return 0;
185 }
186
187 /* Redefine raise() to send signal to calling thread only,
188    as per POSIX 1003.1c */
189 int raise (int sig)
190 {
191   int retcode = pthread_kill(pthread_self(), sig);
192   if (retcode == 0)
193     return 0;
194   else {
195     errno = retcode;
196     return -1;
197   }
198 }