/ lib / sigprocmask.c
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