(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
sigprocmask.c
       1  /* POSIX compatible signal blocking.
       2     Copyright (C) 2006-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2006.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file 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 Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://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 __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  
      63  typedef void (*handler_t) (int);
      64  
      65  #if HAVE_MSVC_INVALID_PARAMETER_HANDLER
      66  static handler_t
      67  signal_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
      90  static handler_t SIGPIPE_handler = SIG_DFL;
      91  #endif
      92  
      93  #if GNULIB_defined_SIGPIPE
      94  static handler_t
      95  ext_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  
     113  int
     114  sigismember (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  
     129  int
     130  sigemptyset (sigset_t *set)
     131  {
     132    *set = 0;
     133    return 0;
     134  }
     135  
     136  int
     137  sigaddset (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  
     156  int
     157  sigdelset (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  
     177  int
     178  sigfillset (sigset_t *set)
     179  {
     180    *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
     181    return 0;
     182  }
     183  
     184  /* Set of currently blocked signals.  */
     185  static volatile sigset_t blocked_set /* = 0 */;
     186  
     187  /* Set of currently blocked and pending signals.  */
     188  static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
     189  
     190  /* Signal handler that is installed for blocked signals.  */
     191  static void
     192  blocked_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  
     204  int
     205  sigpending (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.  */
     219  static volatile handler_t old_handlers[NSIG];
     220  
     221  int
     222  sigprocmask (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.  */
     294  handler_t
     295  rpl_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.  */
     334  int
     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