do-release-commit-and-tag: add option to specify branch
[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 /* Handling of gnulib defined signals.  */
62
63 #if GNULIB_defined_SIGPIPE
64 static handler_t SIGPIPE_handler = SIG_DFL;
65 #endif
66
67 #if GNULIB_defined_SIGPIPE
68 static handler_t
69 ext_signal (int sig, handler_t handler)
70 {
71   switch (sig)
72     {
73     case SIGPIPE:
74       {
75         handler_t old_handler = SIGPIPE_handler;
76         SIGPIPE_handler = handler;
77         return old_handler;
78       }
79     default: /* System defined signal */
80       return signal (sig, handler);
81     }
82 }
83 # define signal ext_signal
84 #endif
85
86 int
87 sigismember (const sigset_t *set, int sig)
88 {
89   if (sig >= 0 && sig < NSIG)
90     {
91       #ifdef SIGABRT_COMPAT
92       if (sig == SIGABRT_COMPAT)
93         sig = SIGABRT;
94       #endif
95
96       return (*set >> sig) & 1;
97     }
98   else
99     return 0;
100 }
101
102 int
103 sigemptyset (sigset_t *set)
104 {
105   *set = 0;
106   return 0;
107 }
108
109 int
110 sigaddset (sigset_t *set, int sig)
111 {
112   if (sig >= 0 && sig < NSIG)
113     {
114       #ifdef SIGABRT_COMPAT
115       if (sig == SIGABRT_COMPAT)
116         sig = SIGABRT;
117       #endif
118
119       *set |= 1U << sig;
120       return 0;
121     }
122   else
123     {
124       errno = EINVAL;
125       return -1;
126     }
127 }
128
129 int
130 sigdelset (sigset_t *set, int sig)
131 {
132   if (sig >= 0 && sig < NSIG)
133     {
134       #ifdef SIGABRT_COMPAT
135       if (sig == SIGABRT_COMPAT)
136         sig = SIGABRT;
137       #endif
138
139       *set &= ~(1U << sig);
140       return 0;
141     }
142   else
143     {
144       errno = EINVAL;
145       return -1;
146     }
147 }
148
149
150 int
151 sigfillset (sigset_t *set)
152 {
153   *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
154   return 0;
155 }
156
157 /* Set of currently blocked signals.  */
158 static volatile sigset_t blocked_set /* = 0 */;
159
160 /* Set of currently blocked and pending signals.  */
161 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
162
163 /* Signal handler that is installed for blocked signals.  */
164 static void
165 blocked_handler (int sig)
166 {
167   /* Reinstall the handler, in case the signal occurs multiple times
168      while blocked.  There is an inherent race where an asynchronous
169      signal in between when the kernel uninstalled the handler and
170      when we reinstall it will trigger the default handler; oh
171      well.  */
172   signal (sig, blocked_handler);
173   if (sig >= 0 && sig < NSIG)
174     pending_array[sig] = 1;
175 }
176
177 int
178 sigpending (sigset_t *set)
179 {
180   sigset_t pending = 0;
181   int sig;
182
183   for (sig = 0; sig < NSIG; sig++)
184     if (pending_array[sig])
185       pending |= 1U << sig;
186   *set = pending;
187   return 0;
188 }
189
190 /* The previous signal handlers.
191    Only the array elements corresponding to blocked signals are relevant.  */
192 static volatile handler_t old_handlers[NSIG];
193
194 int
195 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
196 {
197   if (old_set != NULL)
198     *old_set = blocked_set;
199
200   if (set != NULL)
201     {
202       sigset_t new_blocked_set;
203       sigset_t to_unblock;
204       sigset_t to_block;
205
206       switch (operation)
207         {
208         case SIG_BLOCK:
209           new_blocked_set = blocked_set | *set;
210           break;
211         case SIG_SETMASK:
212           new_blocked_set = *set;
213           break;
214         case SIG_UNBLOCK:
215           new_blocked_set = blocked_set & ~*set;
216           break;
217         default:
218           errno = EINVAL;
219           return -1;
220         }
221       to_unblock = blocked_set & ~new_blocked_set;
222       to_block = new_blocked_set & ~blocked_set;
223
224       if (to_block != 0)
225         {
226           int sig;
227
228           for (sig = 0; sig < NSIG; sig++)
229             if ((to_block >> sig) & 1)
230               {
231                 pending_array[sig] = 0;
232                 if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
233                   blocked_set |= 1U << sig;
234               }
235         }
236
237       if (to_unblock != 0)
238         {
239           sig_atomic_t received[NSIG];
240           int sig;
241
242           for (sig = 0; sig < NSIG; sig++)
243             if ((to_unblock >> sig) & 1)
244               {
245                 if (signal (sig, old_handlers[sig]) != blocked_handler)
246                   /* The application changed a signal handler while the signal
247                      was blocked, bypassing our rpl_signal replacement.
248                      We don't support this.  */
249                   abort ();
250                 received[sig] = pending_array[sig];
251                 blocked_set &= ~(1U << sig);
252                 pending_array[sig] = 0;
253               }
254             else
255               received[sig] = 0;
256
257           for (sig = 0; sig < NSIG; sig++)
258             if (received[sig])
259               raise (sig);
260         }
261     }
262   return 0;
263 }
264
265 /* Install the handler FUNC for signal SIG, and return the previous
266    handler.  */
267 handler_t
268 rpl_signal (int sig, handler_t handler)
269 {
270   /* We must provide a wrapper, so that a user can query what handler
271      they installed even if that signal is currently blocked.  */
272   if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
273       && handler != SIG_ERR)
274     {
275       #ifdef SIGABRT_COMPAT
276       if (sig == SIGABRT_COMPAT)
277         sig = SIGABRT;
278       #endif
279
280       if (blocked_set & (1U << sig))
281         {
282           /* POSIX states that sigprocmask and signal are both
283              async-signal-safe.  This is not true of our
284              implementation - there is a slight data race where an
285              asynchronous interrupt on signal A can occur after we
286              install blocked_handler but before we have updated
287              old_handlers for signal B, such that handler A can see
288              stale information if it calls signal(B).  Oh well -
289              signal handlers really shouldn't try to manipulate the
290              installed handlers of unrelated signals.  */
291           handler_t result = old_handlers[sig];
292           old_handlers[sig] = handler;
293           return result;
294         }
295       else
296         return signal (sig, handler);
297     }
298   else
299     {
300       errno = EINVAL;
301       return SIG_ERR;
302     }
303 }
304
305 #if GNULIB_defined_SIGPIPE
306 /* Raise the signal SIG.  */
307 int
308 rpl_raise (int sig)
309 # undef raise
310 {
311   switch (sig)
312     {
313     case SIGPIPE:
314       if (blocked_set & (1U << sig))
315         pending_array[sig] = 1;
316       else
317         {
318           handler_t handler = SIGPIPE_handler;
319           if (handler == SIG_DFL)
320             exit (128 + SIGPIPE);
321           else if (handler != SIG_IGN)
322             (*handler) (sig);
323         }
324       return 0;
325     default: /* System defined signal */
326       return raise (sig);
327     }
328 }
329 #endif