1/* POSIX compatible signal blocking.
2   Copyright (C) 2006-2012 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#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
28# include "msvc-inval.h"
29#endif
30
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.  */
36
37/* We use raw signal(), but also provide a wrapper rpl_signal() so
38   that applications can query or change a blocked signal.  */
39#undef signal
40
41/* Provide invalid signal numbers as fallbacks if the uncatchable
42   signals are not defined.  */
43#ifndef SIGKILL
44# define SIGKILL (-1)
45#endif
46#ifndef SIGSTOP
47# define SIGSTOP (-1)
48#endif
49
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 __WIN32__) && ! defined __CYGWIN__
54# undef SIGABRT_COMPAT
55# define SIGABRT_COMPAT 6
56#endif
57#ifdef SIGABRT_COMPAT
58# define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT)
59#else
60# define SIGABRT_COMPAT_MASK 0
61#endif
62
63typedef void (*handler_t) (int);
64
65#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
66static handler_t
67signal_nothrow (int sig, handler_t handler)
68{
69  handler_t result;
70
71  TRY_MSVC_INVAL
72    {
73      result = signal (sig, handler);
74    }
75  CATCH_MSVC_INVAL
76    {
77      result = SIG_ERR;
78      errno = EINVAL;
79    }
80  DONE_MSVC_INVAL;
81
82  return result;
83}
84# define signal signal_nothrow
85#endif
86
87/* Handling of gnulib defined signals.  */
88
89#if GNULIB_defined_SIGPIPE
90static handler_t SIGPIPE_handler = SIG_DFL;
91#endif
92
93#if GNULIB_defined_SIGPIPE
94static handler_t
95ext_signal (int sig, handler_t handler)
96{
97  switch (sig)
98    {
99    case SIGPIPE:
100      {
101        handler_t old_handler = SIGPIPE_handler;
102        SIGPIPE_handler = handler;
103        return old_handler;
104      }
105    default: /* System defined signal */
106      return signal (sig, handler);
107    }
108}
109# undef signal
110# define signal ext_signal
111#endif
112
113int
114sigismember (const sigset_t *set, int sig)
115{
116  if (sig >= 0 && sig < NSIG)
117    {
118      #ifdef SIGABRT_COMPAT
119      if (sig == SIGABRT_COMPAT)
120        sig = SIGABRT;
121      #endif
122
123      return (*set >> sig) & 1;
124    }
125  else
126    return 0;
127}
128
129int
130sigemptyset (sigset_t *set)
131{
132  *set = 0;
133  return 0;
134}
135
136int
137sigaddset (sigset_t *set, int sig)
138{
139  if (sig >= 0 && sig < NSIG)
140    {
141      #ifdef SIGABRT_COMPAT
142      if (sig == SIGABRT_COMPAT)
143        sig = SIGABRT;
144      #endif
145
146      *set |= 1U << sig;
147      return 0;
148    }
149  else
150    {
151      errno = EINVAL;
152      return -1;
153    }
154}
155
156int
157sigdelset (sigset_t *set, int sig)
158{
159  if (sig >= 0 && sig < NSIG)
160    {
161      #ifdef SIGABRT_COMPAT
162      if (sig == SIGABRT_COMPAT)
163        sig = SIGABRT;
164      #endif
165
166      *set &= ~(1U << sig);
167      return 0;
168    }
169  else
170    {
171      errno = EINVAL;
172      return -1;
173    }
174}
175
176
177int
178sigfillset (sigset_t *set)
179{
180  *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
181  return 0;
182}
183
184/* Set of currently blocked signals.  */
185static volatile sigset_t blocked_set /* = 0 */;
186
187/* Set of currently blocked and pending signals.  */
188static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
189
190/* Signal handler that is installed for blocked signals.  */
191static void
192blocked_handler (int sig)
193{
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
198     well.  */
199  signal (sig, blocked_handler);
200  if (sig >= 0 && sig < NSIG)
201    pending_array[sig] = 1;
202}
203
204int
205sigpending (sigset_t *set)
206{
207  sigset_t pending = 0;
208  int sig;
209
210  for (sig = 0; sig < NSIG; sig++)
211    if (pending_array[sig])
212      pending |= 1U << sig;
213  *set = pending;
214  return 0;
215}
216
217/* The previous signal handlers.
218   Only the array elements corresponding to blocked signals are relevant.  */
219static volatile handler_t old_handlers[NSIG];
220
221int
222sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
223{
224  if (old_set != NULL)
225    *old_set = blocked_set;
226
227  if (set != NULL)
228    {
229      sigset_t new_blocked_set;
230      sigset_t to_unblock;
231      sigset_t to_block;
232
233      switch (operation)
234        {
235        case SIG_BLOCK:
236          new_blocked_set = blocked_set | *set;
237          break;
238        case SIG_SETMASK:
239          new_blocked_set = *set;
240          break;
241        case SIG_UNBLOCK:
242          new_blocked_set = blocked_set & ~*set;
243          break;
244        default:
245          errno = EINVAL;
246          return -1;
247        }
248      to_unblock = blocked_set & ~new_blocked_set;
249      to_block = new_blocked_set & ~blocked_set;
250
251      if (to_block != 0)
252        {
253          int sig;
254
255          for (sig = 0; sig < NSIG; sig++)
256            if ((to_block >> sig) & 1)
257              {
258                pending_array[sig] = 0;
259                if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
260                  blocked_set |= 1U << sig;
261              }
262        }
263
264      if (to_unblock != 0)
265        {
266          sig_atomic_t received[NSIG];
267          int sig;
268
269          for (sig = 0; sig < NSIG; sig++)
270            if ((to_unblock >> sig) & 1)
271              {
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.  */
276                  abort ();
277                received[sig] = pending_array[sig];
278                blocked_set &= ~(1U << sig);
279                pending_array[sig] = 0;
280              }
281            else
282              received[sig] = 0;
283
284          for (sig = 0; sig < NSIG; sig++)
285            if (received[sig])
286              raise (sig);
287        }
288    }
289  return 0;
290}
291
292/* Install the handler FUNC for signal SIG, and return the previous
293   handler.  */
294handler_t
295rpl_signal (int sig, handler_t handler)
296{
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)
301    {
302      #ifdef SIGABRT_COMPAT
303      if (sig == SIGABRT_COMPAT)
304        sig = SIGABRT;
305      #endif
306
307      if (blocked_set & (1U << sig))
308        {
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;
320          return result;
321        }
322      else
323        return signal (sig, handler);
324    }
325  else
326    {
327      errno = EINVAL;
328      return SIG_ERR;
329    }
330}
331
332#if GNULIB_defined_SIGPIPE
333/* Raise the signal SIGPIPE.  */
334int
335_gl_raise_SIGPIPE (void)
336{
337  if (blocked_set & (1U << SIGPIPE))
338    pending_array[SIGPIPE] = 1;
339  else
340    {
341      handler_t handler = SIGPIPE_handler;
342      if (handler == SIG_DFL)
343        exit (128 + SIGPIPE);
344      else if (handler != SIG_IGN)
345        (*handler) (SIGPIPE);
346    }
347  return 0;
348}
349#endif
350