Blame gettext-runtime/gnulib-lib/sigprocmask.c

Packit 5b56b6
/* POSIX compatible signal blocking.
Packit 5b56b6
   Copyright (C) 2006-2015 Free Software Foundation, Inc.
Packit 5b56b6
   Written by Bruno Haible <bruno@clisp.org>, 2006.
Packit 5b56b6
Packit 5b56b6
   This program is free software: you can redistribute it and/or modify
Packit 5b56b6
   it under the terms of the GNU General Public License as published by
Packit 5b56b6
   the Free Software Foundation; either version 3 of the License, or
Packit 5b56b6
   (at your option) any later version.
Packit 5b56b6
Packit 5b56b6
   This program is distributed in the hope that it will be useful,
Packit 5b56b6
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5b56b6
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 5b56b6
   GNU General Public License for more details.
Packit 5b56b6
Packit 5b56b6
   You should have received a copy of the GNU General Public License
Packit 5b56b6
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 5b56b6
Packit 5b56b6
#include <config.h>
Packit 5b56b6
Packit 5b56b6
/* Specification.  */
Packit 5b56b6
#include <signal.h>
Packit 5b56b6
Packit 5b56b6
#include <errno.h>
Packit 5b56b6
#include <stdint.h>
Packit 5b56b6
#include <stdlib.h>
Packit 5b56b6
Packit 5b56b6
#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
Packit 5b56b6
# include "msvc-inval.h"
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
/* We assume that a platform without POSIX signal blocking functions
Packit 5b56b6
   also does not have the POSIX sigaction() function, only the
Packit 5b56b6
   signal() function.  We also assume signal() has SysV semantics,
Packit 5b56b6
   where any handler is uninstalled prior to being invoked.  This is
Packit 5b56b6
   true for native Windows platforms.  */
Packit 5b56b6
Packit 5b56b6
/* We use raw signal(), but also provide a wrapper rpl_signal() so
Packit 5b56b6
   that applications can query or change a blocked signal.  */
Packit 5b56b6
#undef signal
Packit 5b56b6
Packit 5b56b6
/* Provide invalid signal numbers as fallbacks if the uncatchable
Packit 5b56b6
   signals are not defined.  */
Packit 5b56b6
#ifndef SIGKILL
Packit 5b56b6
# define SIGKILL (-1)
Packit 5b56b6
#endif
Packit 5b56b6
#ifndef SIGSTOP
Packit 5b56b6
# define SIGSTOP (-1)
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
/* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
Packit 5b56b6
   for the signal SIGABRT.  Only one signal handler is stored for both
Packit 5b56b6
   SIGABRT and SIGABRT_COMPAT.  SIGABRT_COMPAT is not a signal of its own.  */
Packit 5b56b6
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
Packit 5b56b6
# undef SIGABRT_COMPAT
Packit 5b56b6
# define SIGABRT_COMPAT 6
Packit 5b56b6
#endif
Packit 5b56b6
#ifdef SIGABRT_COMPAT
Packit 5b56b6
# define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT)
Packit 5b56b6
#else
Packit 5b56b6
# define SIGABRT_COMPAT_MASK 0
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
typedef void (*handler_t) (int);
Packit 5b56b6
Packit 5b56b6
#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
Packit 5b56b6
static handler_t
Packit 5b56b6
signal_nothrow (int sig, handler_t handler)
Packit 5b56b6
{
Packit 5b56b6
  handler_t result;
Packit 5b56b6
Packit 5b56b6
  TRY_MSVC_INVAL
Packit 5b56b6
    {
Packit 5b56b6
      result = signal (sig, handler);
Packit 5b56b6
    }
Packit 5b56b6
  CATCH_MSVC_INVAL
Packit 5b56b6
    {
Packit 5b56b6
      result = SIG_ERR;
Packit 5b56b6
      errno = EINVAL;
Packit 5b56b6
    }
Packit 5b56b6
  DONE_MSVC_INVAL;
Packit 5b56b6
Packit 5b56b6
  return result;
Packit 5b56b6
}
Packit 5b56b6
# define signal signal_nothrow
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
/* Handling of gnulib defined signals.  */
Packit 5b56b6
Packit 5b56b6
#if GNULIB_defined_SIGPIPE
Packit 5b56b6
static handler_t SIGPIPE_handler = SIG_DFL;
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#if GNULIB_defined_SIGPIPE
Packit 5b56b6
static handler_t
Packit 5b56b6
ext_signal (int sig, handler_t handler)
Packit 5b56b6
{
Packit 5b56b6
  switch (sig)
Packit 5b56b6
    {
Packit 5b56b6
    case SIGPIPE:
Packit 5b56b6
      {
Packit 5b56b6
        handler_t old_handler = SIGPIPE_handler;
Packit 5b56b6
        SIGPIPE_handler = handler;
Packit 5b56b6
        return old_handler;
Packit 5b56b6
      }
Packit 5b56b6
    default: /* System defined signal */
Packit 5b56b6
      return signal (sig, handler);
Packit 5b56b6
    }
Packit 5b56b6
}
Packit 5b56b6
# undef signal
Packit 5b56b6
# define signal ext_signal
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
sigismember (const sigset_t *set, int sig)
Packit 5b56b6
{
Packit 5b56b6
  if (sig >= 0 && sig < NSIG)
Packit 5b56b6
    {
Packit 5b56b6
      #ifdef SIGABRT_COMPAT
Packit 5b56b6
      if (sig == SIGABRT_COMPAT)
Packit 5b56b6
        sig = SIGABRT;
Packit 5b56b6
      #endif
Packit 5b56b6
Packit 5b56b6
      return (*set >> sig) & 1;
Packit 5b56b6
    }
Packit 5b56b6
  else
Packit 5b56b6
    return 0;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
sigemptyset (sigset_t *set)
Packit 5b56b6
{
Packit 5b56b6
  *set = 0;
Packit 5b56b6
  return 0;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
sigaddset (sigset_t *set, int sig)
Packit 5b56b6
{
Packit 5b56b6
  if (sig >= 0 && sig < NSIG)
Packit 5b56b6
    {
Packit 5b56b6
      #ifdef SIGABRT_COMPAT
Packit 5b56b6
      if (sig == SIGABRT_COMPAT)
Packit 5b56b6
        sig = SIGABRT;
Packit 5b56b6
      #endif
Packit 5b56b6
Packit 5b56b6
      *set |= 1U << sig;
Packit 5b56b6
      return 0;
Packit 5b56b6
    }
Packit 5b56b6
  else
Packit 5b56b6
    {
Packit 5b56b6
      errno = EINVAL;
Packit 5b56b6
      return -1;
Packit 5b56b6
    }
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
sigdelset (sigset_t *set, int sig)
Packit 5b56b6
{
Packit 5b56b6
  if (sig >= 0 && sig < NSIG)
Packit 5b56b6
    {
Packit 5b56b6
      #ifdef SIGABRT_COMPAT
Packit 5b56b6
      if (sig == SIGABRT_COMPAT)
Packit 5b56b6
        sig = SIGABRT;
Packit 5b56b6
      #endif
Packit 5b56b6
Packit 5b56b6
      *set &= ~(1U << sig);
Packit 5b56b6
      return 0;
Packit 5b56b6
    }
Packit 5b56b6
  else
Packit 5b56b6
    {
Packit 5b56b6
      errno = EINVAL;
Packit 5b56b6
      return -1;
Packit 5b56b6
    }
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
sigfillset (sigset_t *set)
Packit 5b56b6
{
Packit 5b56b6
  *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
Packit 5b56b6
  return 0;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* Set of currently blocked signals.  */
Packit 5b56b6
static volatile sigset_t blocked_set /* = 0 */;
Packit 5b56b6
Packit 5b56b6
/* Set of currently blocked and pending signals.  */
Packit 5b56b6
static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
Packit 5b56b6
Packit 5b56b6
/* Signal handler that is installed for blocked signals.  */
Packit 5b56b6
static void
Packit 5b56b6
blocked_handler (int sig)
Packit 5b56b6
{
Packit 5b56b6
  /* Reinstall the handler, in case the signal occurs multiple times
Packit 5b56b6
     while blocked.  There is an inherent race where an asynchronous
Packit 5b56b6
     signal in between when the kernel uninstalled the handler and
Packit 5b56b6
     when we reinstall it will trigger the default handler; oh
Packit 5b56b6
     well.  */
Packit 5b56b6
  signal (sig, blocked_handler);
Packit 5b56b6
  if (sig >= 0 && sig < NSIG)
Packit 5b56b6
    pending_array[sig] = 1;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
sigpending (sigset_t *set)
Packit 5b56b6
{
Packit 5b56b6
  sigset_t pending = 0;
Packit 5b56b6
  int sig;
Packit 5b56b6
Packit 5b56b6
  for (sig = 0; sig < NSIG; sig++)
Packit 5b56b6
    if (pending_array[sig])
Packit 5b56b6
      pending |= 1U << sig;
Packit 5b56b6
  *set = pending;
Packit 5b56b6
  return 0;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* The previous signal handlers.
Packit 5b56b6
   Only the array elements corresponding to blocked signals are relevant.  */
Packit 5b56b6
static volatile handler_t old_handlers[NSIG];
Packit 5b56b6
Packit 5b56b6
int
Packit 5b56b6
sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
Packit 5b56b6
{
Packit 5b56b6
  if (old_set != NULL)
Packit 5b56b6
    *old_set = blocked_set;
Packit 5b56b6
Packit 5b56b6
  if (set != NULL)
Packit 5b56b6
    {
Packit 5b56b6
      sigset_t new_blocked_set;
Packit 5b56b6
      sigset_t to_unblock;
Packit 5b56b6
      sigset_t to_block;
Packit 5b56b6
Packit 5b56b6
      switch (operation)
Packit 5b56b6
        {
Packit 5b56b6
        case SIG_BLOCK:
Packit 5b56b6
          new_blocked_set = blocked_set | *set;
Packit 5b56b6
          break;
Packit 5b56b6
        case SIG_SETMASK:
Packit 5b56b6
          new_blocked_set = *set;
Packit 5b56b6
          break;
Packit 5b56b6
        case SIG_UNBLOCK:
Packit 5b56b6
          new_blocked_set = blocked_set & ~*set;
Packit 5b56b6
          break;
Packit 5b56b6
        default:
Packit 5b56b6
          errno = EINVAL;
Packit 5b56b6
          return -1;
Packit 5b56b6
        }
Packit 5b56b6
      to_unblock = blocked_set & ~new_blocked_set;
Packit 5b56b6
      to_block = new_blocked_set & ~blocked_set;
Packit 5b56b6
Packit 5b56b6
      if (to_block != 0)
Packit 5b56b6
        {
Packit 5b56b6
          int sig;
Packit 5b56b6
Packit 5b56b6
          for (sig = 0; sig < NSIG; sig++)
Packit 5b56b6
            if ((to_block >> sig) & 1)
Packit 5b56b6
              {
Packit 5b56b6
                pending_array[sig] = 0;
Packit 5b56b6
                if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
Packit 5b56b6
                  blocked_set |= 1U << sig;
Packit 5b56b6
              }
Packit 5b56b6
        }
Packit 5b56b6
Packit 5b56b6
      if (to_unblock != 0)
Packit 5b56b6
        {
Packit 5b56b6
          sig_atomic_t received[NSIG];
Packit 5b56b6
          int sig;
Packit 5b56b6
Packit 5b56b6
          for (sig = 0; sig < NSIG; sig++)
Packit 5b56b6
            if ((to_unblock >> sig) & 1)
Packit 5b56b6
              {
Packit 5b56b6
                if (signal (sig, old_handlers[sig]) != blocked_handler)
Packit 5b56b6
                  /* The application changed a signal handler while the signal
Packit 5b56b6
                     was blocked, bypassing our rpl_signal replacement.
Packit 5b56b6
                     We don't support this.  */
Packit 5b56b6
                  abort ();
Packit 5b56b6
                received[sig] = pending_array[sig];
Packit 5b56b6
                blocked_set &= ~(1U << sig);
Packit 5b56b6
                pending_array[sig] = 0;
Packit 5b56b6
              }
Packit 5b56b6
            else
Packit 5b56b6
              received[sig] = 0;
Packit 5b56b6
Packit 5b56b6
          for (sig = 0; sig < NSIG; sig++)
Packit 5b56b6
            if (received[sig])
Packit 5b56b6
              raise (sig);
Packit 5b56b6
        }
Packit 5b56b6
    }
Packit 5b56b6
  return 0;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* Install the handler FUNC for signal SIG, and return the previous
Packit 5b56b6
   handler.  */
Packit 5b56b6
handler_t
Packit 5b56b6
rpl_signal (int sig, handler_t handler)
Packit 5b56b6
{
Packit 5b56b6
  /* We must provide a wrapper, so that a user can query what handler
Packit 5b56b6
     they installed even if that signal is currently blocked.  */
Packit 5b56b6
  if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
Packit 5b56b6
      && handler != SIG_ERR)
Packit 5b56b6
    {
Packit 5b56b6
      #ifdef SIGABRT_COMPAT
Packit 5b56b6
      if (sig == SIGABRT_COMPAT)
Packit 5b56b6
        sig = SIGABRT;
Packit 5b56b6
      #endif
Packit 5b56b6
Packit 5b56b6
      if (blocked_set & (1U << sig))
Packit 5b56b6
        {
Packit 5b56b6
          /* POSIX states that sigprocmask and signal are both
Packit 5b56b6
             async-signal-safe.  This is not true of our
Packit 5b56b6
             implementation - there is a slight data race where an
Packit 5b56b6
             asynchronous interrupt on signal A can occur after we
Packit 5b56b6
             install blocked_handler but before we have updated
Packit 5b56b6
             old_handlers for signal B, such that handler A can see
Packit 5b56b6
             stale information if it calls signal(B).  Oh well -
Packit 5b56b6
             signal handlers really shouldn't try to manipulate the
Packit 5b56b6
             installed handlers of unrelated signals.  */
Packit 5b56b6
          handler_t result = old_handlers[sig];
Packit 5b56b6
          old_handlers[sig] = handler;
Packit 5b56b6
          return result;
Packit 5b56b6
        }
Packit 5b56b6
      else
Packit 5b56b6
        return signal (sig, handler);
Packit 5b56b6
    }
Packit 5b56b6
  else
Packit 5b56b6
    {
Packit 5b56b6
      errno = EINVAL;
Packit 5b56b6
      return SIG_ERR;
Packit 5b56b6
    }
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
#if GNULIB_defined_SIGPIPE
Packit 5b56b6
/* Raise the signal SIGPIPE.  */
Packit 5b56b6
int
Packit 5b56b6
_gl_raise_SIGPIPE (void)
Packit 5b56b6
{
Packit 5b56b6
  if (blocked_set & (1U << SIGPIPE))
Packit 5b56b6
    pending_array[SIGPIPE] = 1;
Packit 5b56b6
  else
Packit 5b56b6
    {
Packit 5b56b6
      handler_t handler = SIGPIPE_handler;
Packit 5b56b6
      if (handler == SIG_DFL)
Packit 5b56b6
        exit (128 + SIGPIPE);
Packit 5b56b6
      else if (handler != SIG_IGN)
Packit 5b56b6
        (*handler) (SIGPIPE);
Packit 5b56b6
    }
Packit 5b56b6
  return 0;
Packit 5b56b6
}
Packit 5b56b6
#endif