Blame gnulib/lib/sigprocmask.c

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