Blame gl/tests/sigprocmask.c

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