1 /* POSIX compatible signal blocking.
2 Copyright (C) 2006-2018 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
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.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
27 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
28 # include "msvc-inval.h"
31 /* We assume that a platform without POSIX signal blocking functions
32 also does not have the POSIX sigaction() function, only the
33 signal() function. We also assume signal() has SysV semantics,
34 where any handler is uninstalled prior to being invoked. This is
35 true for native Windows platforms. */
37 /* We use raw signal(), but also provide a wrapper rpl_signal() so
38 that applications can query or change a blocked signal. */
41 /* Provide invalid signal numbers as fallbacks if the uncatchable
42 signals are not defined. */
50 /* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
51 for the signal SIGABRT. Only one signal handler is stored for both
52 SIGABRT and SIGABRT_COMPAT. SIGABRT_COMPAT is not a signal of its own. */
53 #if defined _WIN32 && ! defined __CYGWIN__
54 # undef SIGABRT_COMPAT
55 # define SIGABRT_COMPAT 6
58 # define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT)
60 # define SIGABRT_COMPAT_MASK 0
63 typedef void (*handler_t
) (int);
65 #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
67 signal_nothrow (int sig
, handler_t handler
)
73 result
= signal (sig
, handler
);
84 # define signal signal_nothrow
87 /* Handling of gnulib defined signals. */
89 #if GNULIB_defined_SIGPIPE
90 static handler_t SIGPIPE_handler
= SIG_DFL
;
93 #if GNULIB_defined_SIGPIPE
95 ext_signal (int sig
, handler_t handler
)
101 handler_t old_handler
= SIGPIPE_handler
;
102 SIGPIPE_handler
= handler
;
105 default: /* System defined signal */
106 return signal (sig
, handler
);
110 # define signal ext_signal
114 sigismember (const sigset_t
*set
, int sig
)
116 if (sig
>= 0 && sig
< NSIG
)
118 #ifdef SIGABRT_COMPAT
119 if (sig
== SIGABRT_COMPAT
)
123 return (*set
>> sig
) & 1;
130 sigemptyset (sigset_t
*set
)
137 sigaddset (sigset_t
*set
, int sig
)
139 if (sig
>= 0 && sig
< NSIG
)
141 #ifdef SIGABRT_COMPAT
142 if (sig
== SIGABRT_COMPAT
)
157 sigdelset (sigset_t
*set
, int sig
)
159 if (sig
>= 0 && sig
< NSIG
)
161 #ifdef SIGABRT_COMPAT
162 if (sig
== SIGABRT_COMPAT
)
166 *set
&= ~(1U << sig
);
178 sigfillset (sigset_t
*set
)
180 *set
= ((2U << (NSIG
- 1)) - 1) & ~ SIGABRT_COMPAT_MASK
;
184 /* Set of currently blocked signals. */
185 static volatile sigset_t blocked_set
/* = 0 */;
187 /* Set of currently blocked and pending signals. */
188 static volatile sig_atomic_t pending_array
[NSIG
] /* = { 0 } */;
190 /* Signal handler that is installed for blocked signals. */
192 blocked_handler (int sig
)
194 /* Reinstall the handler, in case the signal occurs multiple times
195 while blocked. There is an inherent race where an asynchronous
196 signal in between when the kernel uninstalled the handler and
197 when we reinstall it will trigger the default handler; oh
199 signal (sig
, blocked_handler
);
200 if (sig
>= 0 && sig
< NSIG
)
201 pending_array
[sig
] = 1;
205 sigpending (sigset_t
*set
)
207 sigset_t pending
= 0;
210 for (sig
= 0; sig
< NSIG
; sig
++)
211 if (pending_array
[sig
])
212 pending
|= 1U << sig
;
217 /* The previous signal handlers.
218 Only the array elements corresponding to blocked signals are relevant. */
219 static volatile handler_t old_handlers
[NSIG
];
222 sigprocmask (int operation
, const sigset_t
*set
, sigset_t
*old_set
)
225 *old_set
= blocked_set
;
229 sigset_t new_blocked_set
;
236 new_blocked_set
= blocked_set
| *set
;
239 new_blocked_set
= *set
;
242 new_blocked_set
= blocked_set
& ~*set
;
248 to_unblock
= blocked_set
& ~new_blocked_set
;
249 to_block
= new_blocked_set
& ~blocked_set
;
255 for (sig
= 0; sig
< NSIG
; sig
++)
256 if ((to_block
>> sig
) & 1)
258 pending_array
[sig
] = 0;
259 if ((old_handlers
[sig
] = signal (sig
, blocked_handler
)) != SIG_ERR
)
260 blocked_set
|= 1U << sig
;
266 sig_atomic_t received
[NSIG
];
269 for (sig
= 0; sig
< NSIG
; sig
++)
270 if ((to_unblock
>> sig
) & 1)
272 if (signal (sig
, old_handlers
[sig
]) != blocked_handler
)
273 /* The application changed a signal handler while the signal
274 was blocked, bypassing our rpl_signal replacement.
275 We don't support this. */
277 received
[sig
] = pending_array
[sig
];
278 blocked_set
&= ~(1U << sig
);
279 pending_array
[sig
] = 0;
284 for (sig
= 0; sig
< NSIG
; sig
++)
292 /* Install the handler FUNC for signal SIG, and return the previous
295 rpl_signal (int sig
, handler_t handler
)
297 /* We must provide a wrapper, so that a user can query what handler
298 they installed even if that signal is currently blocked. */
299 if (sig
>= 0 && sig
< NSIG
&& sig
!= SIGKILL
&& sig
!= SIGSTOP
300 && handler
!= SIG_ERR
)
302 #ifdef SIGABRT_COMPAT
303 if (sig
== SIGABRT_COMPAT
)
307 if (blocked_set
& (1U << sig
))
309 /* POSIX states that sigprocmask and signal are both
310 async-signal-safe. This is not true of our
311 implementation - there is a slight data race where an
312 asynchronous interrupt on signal A can occur after we
313 install blocked_handler but before we have updated
314 old_handlers for signal B, such that handler A can see
315 stale information if it calls signal(B). Oh well -
316 signal handlers really shouldn't try to manipulate the
317 installed handlers of unrelated signals. */
318 handler_t result
= old_handlers
[sig
];
319 old_handlers
[sig
] = handler
;
323 return signal (sig
, handler
);
332 #if GNULIB_defined_SIGPIPE
333 /* Raise the signal SIGPIPE. */
335 _gl_raise_SIGPIPE (void)
337 if (blocked_set
& (1U << SIGPIPE
))
338 pending_array
[SIGPIPE
] = 1;
341 handler_t handler
= SIGPIPE_handler
;
342 if (handler
== SIG_DFL
)
343 exit (128 + SIGPIPE
);
344 else if (handler
!= SIG_IGN
)
345 (*handler
) (SIGPIPE
);