a9aff47615ba0de9ad744e21f20d7e9caaa36df4
[gnulib.git] / lib / sigprocmask.c
1 /* POSIX compatible signal blocking.
2    Copyright (C) 2006-2011 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2006.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (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 General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <signal.h>
22
23 #include <errno.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26
27 /* We assume that a platform without POSIX signal blocking functions
28    also does not have the POSIX sigaction() function, only the
29    signal() function.  We also assume signal() has SysV semantics,
30    where any handler is uninstalled prior to being invoked.  This is
31    true for Woe32 platforms.  */
32
33 /* We use raw signal(), but also provide a wrapper rpl_signal() so
34    that applications can query or change a blocked signal.  */
35 #undef signal
36
37 /* Provide invalid signal numbers as fallbacks if the uncatchable
38    signals are not defined.  */
39 #ifndef SIGKILL
40 # define SIGKILL (-1)
41 #endif
42 #ifndef SIGSTOP
43 # define SIGSTOP (-1)
44 #endif
45
46 /* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
47    for the signal SIGABRT.  Only one signal handler is stored for both
48    SIGABRT and SIGABRT_COMPAT.  SIGABRT_COMPAT is not a signal of its own.  */
49 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
50 # undef SIGABRT_COMPAT
51 # define SIGABRT_COMPAT 6
52 #endif
53 #ifdef SIGABRT_COMPAT
54 # define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT)
55 #else
56 # define SIGABRT_COMPAT_MASK 0
57 #endif
58
59 typedef void (*handler_t) (int);
60
61 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
62 # include "msvc-inval.h"
63
64 static inline handler_t
65 signal_nothrow (int sig, handler_t handler)
66 {
67   handler_t result;
68
69   TRY_MSVC_INVAL
70     {
71       result = signal (sig, handler);
72     }
73   CATCH_MSVC_INVAL
74     {
75       result = SIG_ERR;
76       errno = EINVAL;
77     }
78   DONE_MSVC_INVAL;
79
80   return result;
81 }
82 # define signal signal_nothrow
83 #endif
84
85 /* Handling of gnulib defined signals.  */
86
87 #if GNULIB_defined_SIGPIPE
88 static handler_t SIGPIPE_handler = SIG_DFL;
89 #endif
90
91 #if GNULIB_defined_SIGPIPE
92 static handler_t
93 ext_signal (int sig, handler_t handler)
94 {
95   switch (sig)
96     {
97     case SIGPIPE:
98       {
99         handler_t old_handler = SIGPIPE_handler;
100         SIGPIPE_handler = handler;
101         return old_handler;
102       }
103     default: /* System defined signal */
104       return signal (sig, handler);
105     }
106 }
107 # undef signal
108 # define signal ext_signal
109 #endif
110
111 int
112 sigismember (const sigset_t *set, int sig)
113 {
114   if (sig >= 0 && sig < NSIG)
115     {
116       #ifdef SIGABRT_COMPAT
117       if (sig == SIGABRT_COMPAT)
118         sig = SIGABRT;
119       #endif
120
121       return (*set >> sig) & 1;
122     }
123   else
124     return 0;
125 }
126
127 int
128 sigemptyset (sigset_t *set)
129 {
130   *set = 0;
131   return 0;
132 }
133
134 int
135 sigaddset (sigset_t *set, int sig)
136 {
137   if (sig >= 0 && sig < NSIG)
138     {
139       #ifdef SIGABRT_COMPAT
140       if (sig == SIGABRT_COMPAT)
141         sig = SIGABRT;
142       #endif
143
144       *set |= 1U << sig;
145       return 0;
146     }
147   else
148     {
149       errno = EINVAL;
150       return -1;
151     }
152 }
153
154 int
155 sigdelset (sigset_t *set, int sig)
156 {
157   if (sig >= 0 && sig < NSIG)
158     {
159       #ifdef SIGABRT_COMPAT
160       if (sig == SIGABRT_COMPAT)
161         sig = SIGABRT;
162       #endif
163
164       *set &= ~(1U << sig);
165       return 0;
166     }
167   else
168     {
169       errno = EINVAL;
170       return -1;
171     }
172 }
173
174
175 int
176 sigfillset (sigset_t *set)
177 {
178   *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
179   return 0;
180 }
181
182 /* Set of currently blocked signals.  */
183 static volatile sigset_t blocked_set /* = 0 */;
184
185 /* Set of currently blocked and pending signals.  */
186 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
187
188 /* Signal handler that is installed for blocked signals.  */
189 static void
190 blocked_handler (int sig)
191 {
192   /* Reinstall the handler, in case the signal occurs multiple times
193      while blocked.  There is an inherent race where an asynchronous
194      signal in between when the kernel uninstalled the handler and
195      when we reinstall it will trigger the default handler; oh
196      well.  */
197   signal (sig, blocked_handler);
198   if (sig >= 0 && sig < NSIG)
199     pending_array[sig] = 1;
200 }
201
202 int
203 sigpending (sigset_t *set)
204 {
205   sigset_t pending = 0;
206   int sig;
207
208   for (sig = 0; sig < NSIG; sig++)
209     if (pending_array[sig])
210       pending |= 1U << sig;
211   *set = pending;
212   return 0;
213 }
214
215 /* The previous signal handlers.
216    Only the array elements corresponding to blocked signals are relevant.  */
217 static volatile handler_t old_handlers[NSIG];
218
219 int
220 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
221 {
222   if (old_set != NULL)
223     *old_set = blocked_set;
224
225   if (set != NULL)
226     {
227       sigset_t new_blocked_set;
228       sigset_t to_unblock;
229       sigset_t to_block;
230
231       switch (operation)
232         {
233         case SIG_BLOCK:
234           new_blocked_set = blocked_set | *set;
235           break;
236         case SIG_SETMASK:
237           new_blocked_set = *set;
238           break;
239         case SIG_UNBLOCK:
240           new_blocked_set = blocked_set & ~*set;
241           break;
242         default:
243           errno = EINVAL;
244           return -1;
245         }
246       to_unblock = blocked_set & ~new_blocked_set;
247       to_block = new_blocked_set & ~blocked_set;
248
249       if (to_block != 0)
250         {
251           int sig;
252
253           for (sig = 0; sig < NSIG; sig++)
254             if ((to_block >> sig) & 1)
255               {
256                 pending_array[sig] = 0;
257                 if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
258                   blocked_set |= 1U << sig;
259               }
260         }
261
262       if (to_unblock != 0)
263         {
264           sig_atomic_t received[NSIG];
265           int sig;
266
267           for (sig = 0; sig < NSIG; sig++)
268             if ((to_unblock >> sig) & 1)
269               {
270                 if (signal (sig, old_handlers[sig]) != blocked_handler)
271                   /* The application changed a signal handler while the signal
272                      was blocked, bypassing our rpl_signal replacement.
273                      We don't support this.  */
274                   abort ();
275                 received[sig] = pending_array[sig];
276                 blocked_set &= ~(1U << sig);
277                 pending_array[sig] = 0;
278               }
279             else
280               received[sig] = 0;
281
282           for (sig = 0; sig < NSIG; sig++)
283             if (received[sig])
284               raise (sig);
285         }
286     }
287   return 0;
288 }
289
290 /* Install the handler FUNC for signal SIG, and return the previous
291    handler.  */
292 handler_t
293 rpl_signal (int sig, handler_t handler)
294 {
295   /* We must provide a wrapper, so that a user can query what handler
296      they installed even if that signal is currently blocked.  */
297   if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
298       && handler != SIG_ERR)
299     {
300       #ifdef SIGABRT_COMPAT
301       if (sig == SIGABRT_COMPAT)
302         sig = SIGABRT;
303       #endif
304
305       if (blocked_set & (1U << sig))
306         {
307           /* POSIX states that sigprocmask and signal are both
308              async-signal-safe.  This is not true of our
309              implementation - there is a slight data race where an
310              asynchronous interrupt on signal A can occur after we
311              install blocked_handler but before we have updated
312              old_handlers for signal B, such that handler A can see
313              stale information if it calls signal(B).  Oh well -
314              signal handlers really shouldn't try to manipulate the
315              installed handlers of unrelated signals.  */
316           handler_t result = old_handlers[sig];
317           old_handlers[sig] = handler;
318           return result;
319         }
320       else
321         return signal (sig, handler);
322     }
323   else
324     {
325       errno = EINVAL;
326       return SIG_ERR;
327     }
328 }
329
330 #if GNULIB_defined_SIGPIPE
331 /* Raise the signal SIG.  */
332 int
333 rpl_raise (int sig)
334 # undef raise
335 {
336   switch (sig)
337     {
338     case SIGPIPE:
339       if (blocked_set & (1U << sig))
340         pending_array[sig] = 1;
341       else
342         {
343           handler_t handler = SIGPIPE_handler;
344           if (handler == SIG_DFL)
345             exit (128 + SIGPIPE);
346           else if (handler != SIG_IGN)
347             (*handler) (sig);
348         }
349       return 0;
350     default: /* System defined signal */
351       return raise (sig);
352     }
353 }
354 #endif