Blame gnulib/tests/sigprocmask.c

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