Blame gnulib/tests/sigaction.c

Packit 06dd63
/* POSIX compatible signal blocking.
Packit 06dd63
   Copyright (C) 2008-2019 Free Software Foundation, Inc.
Packit 06dd63
   Written by Eric Blake <ebb9@byu.net>, 2008.
Packit 06dd63
Packit 06dd63
   This program is free software: you can redistribute it and/or modify
Packit 06dd63
   it under the terms of the GNU General Public License as published by
Packit 06dd63
   the Free Software Foundation; either version 3 of the License, or
Packit 06dd63
   (at your option) any later version.
Packit 06dd63
Packit 06dd63
   This program is distributed in the hope that it will be useful,
Packit 06dd63
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 06dd63
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 06dd63
   GNU General Public License for more details.
Packit 06dd63
Packit 06dd63
   You should have received a copy of the GNU General Public License
Packit 06dd63
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
Packit 06dd63
Packit 06dd63
#include <config.h>
Packit 06dd63
Packit 06dd63
/* Specification.  */
Packit 06dd63
#include <signal.h>
Packit 06dd63
Packit 06dd63
#include <errno.h>
Packit 06dd63
#include <stdint.h>
Packit 06dd63
#include <stdlib.h>
Packit 06dd63
Packit 06dd63
/* This implementation of sigaction is tailored to native Windows behavior:
Packit 06dd63
   signal() has SysV semantics (ie. the handler is uninstalled before
Packit 06dd63
   it is invoked).  This is an inherent data race if an asynchronous
Packit 06dd63
   signal is sent twice in a row before we can reinstall our handler,
Packit 06dd63
   but there's nothing we can do about it.  Meanwhile, sigprocmask()
Packit 06dd63
   is not present, and while we can use the gnulib replacement to
Packit 06dd63
   provide critical sections, it too suffers from potential data races
Packit 06dd63
   in the face of an ill-timed asynchronous signal.  And we compound
Packit 06dd63
   the situation by reading static storage in a signal handler, which
Packit 06dd63
   POSIX warns is not generically async-signal-safe.  Oh well.
Packit 06dd63
Packit 06dd63
   Additionally:
Packit 06dd63
     - We don't implement SA_NOCLDSTOP or SA_NOCLDWAIT, because SIGCHLD
Packit 06dd63
       is not defined.
Packit 06dd63
     - We don't implement SA_ONSTACK, because sigaltstack() is not present.
Packit 06dd63
     - We ignore SA_RESTART, because blocking native Windows API calls are
Packit 06dd63
       not interrupted anyway when an asynchronous signal occurs, and the
Packit 06dd63
       MSVCRT runtime never sets errno to EINTR.
Packit 06dd63
     - We don't implement SA_SIGINFO because it is impossible to do so
Packit 06dd63
       portably.
Packit 06dd63
Packit 06dd63
   POSIX states that an application should not mix signal() and
Packit 06dd63
   sigaction().  We support the use of signal() within the gnulib
Packit 06dd63
   sigprocmask() substitute, but all other application code linked
Packit 06dd63
   with this module should stick with only sigaction().  */
Packit 06dd63
Packit 06dd63
/* Check some of our assumptions.  */
Packit 06dd63
#if defined SIGCHLD || defined HAVE_SIGALTSTACK || defined HAVE_SIGINTERRUPT
Packit 06dd63
# error "Revisit the assumptions made in the sigaction module"
Packit 06dd63
#endif
Packit 06dd63
Packit 06dd63
/* Out-of-range substitutes make a good fallback for uncatchable
Packit 06dd63
   signals.  */
Packit 06dd63
#ifndef SIGKILL
Packit 06dd63
# define SIGKILL (-1)
Packit 06dd63
#endif
Packit 06dd63
#ifndef SIGSTOP
Packit 06dd63
# define SIGSTOP (-1)
Packit 06dd63
#endif
Packit 06dd63
Packit 06dd63
/* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
Packit 06dd63
   for the signal SIGABRT.  Only one signal handler is stored for both
Packit 06dd63
   SIGABRT and SIGABRT_COMPAT.  SIGABRT_COMPAT is not a signal of its own.  */
Packit 06dd63
#if defined _WIN32 && ! defined __CYGWIN__
Packit 06dd63
# undef SIGABRT_COMPAT
Packit 06dd63
# define SIGABRT_COMPAT 6
Packit 06dd63
#endif
Packit 06dd63
Packit 06dd63
/* A signal handler.  */
Packit 06dd63
typedef void (*handler_t) (int signal);
Packit 06dd63
Packit 06dd63
/* Set of current actions.  If sa_handler for an entry is NULL, then
Packit 06dd63
   that signal is not currently handled by the sigaction handler.  */
Packit 06dd63
static struct sigaction volatile action_array[NSIG] /* = 0 */;
Packit 06dd63
Packit 06dd63
/* Signal handler that is installed for signals.  */
Packit 06dd63
static void
Packit 06dd63
sigaction_handler (int sig)
Packit 06dd63
{
Packit 06dd63
  handler_t handler;
Packit 06dd63
  sigset_t mask;
Packit 06dd63
  sigset_t oldmask;
Packit 06dd63
  int saved_errno = errno;
Packit 06dd63
  if (sig < 0 || NSIG <= sig || !action_array[sig].sa_handler)
Packit 06dd63
    {
Packit 06dd63
      /* Unexpected situation; be careful to avoid recursive abort.  */
Packit 06dd63
      if (sig == SIGABRT)
Packit 06dd63
        signal (SIGABRT, SIG_DFL);
Packit 06dd63
      abort ();
Packit 06dd63
    }
Packit 06dd63
Packit 06dd63
  /* Reinstall the signal handler when required; otherwise update the
Packit 06dd63
     bookkeeping so that the user's handler may call sigaction and get
Packit 06dd63
     accurate results.  We know the signal isn't currently blocked, or
Packit 06dd63
     we wouldn't be in its handler, therefore we know that we are not
Packit 06dd63
     interrupting a sigaction() call.  There is a race where any
Packit 06dd63
     asynchronous instance of the same signal occurring before we
Packit 06dd63
     reinstall the handler will trigger the default handler; oh
Packit 06dd63
     well.  */
Packit 06dd63
  handler = action_array[sig].sa_handler;
Packit 06dd63
  if ((action_array[sig].sa_flags & SA_RESETHAND) == 0)
Packit 06dd63
    signal (sig, sigaction_handler);
Packit 06dd63
  else
Packit 06dd63
    action_array[sig].sa_handler = NULL;
Packit 06dd63
Packit 06dd63
  /* Block appropriate signals.  */
Packit 06dd63
  mask = action_array[sig].sa_mask;
Packit 06dd63
  if ((action_array[sig].sa_flags & SA_NODEFER) == 0)
Packit 06dd63
    sigaddset (&mask, sig);
Packit 06dd63
  sigprocmask (SIG_BLOCK, &mask, &oldmask);
Packit 06dd63
Packit 06dd63
  /* Invoke the user's handler, then restore prior mask.  */
Packit 06dd63
  errno = saved_errno;
Packit 06dd63
  handler (sig);
Packit 06dd63
  saved_errno = errno;
Packit 06dd63
  sigprocmask (SIG_SETMASK, &oldmask, NULL);
Packit 06dd63
  errno = saved_errno;
Packit 06dd63
}
Packit 06dd63
Packit 06dd63
/* Change and/or query the action that will be taken on delivery of
Packit 06dd63
   signal SIG.  If not NULL, ACT describes the new behavior.  If not
Packit 06dd63
   NULL, OACT is set to the prior behavior.  Return 0 on success, or
Packit 06dd63
   set errno and return -1 on failure.  */
Packit 06dd63
int
Packit 06dd63
sigaction (int sig, const struct sigaction *restrict act,
Packit 06dd63
           struct sigaction *restrict oact)
Packit 06dd63
{
Packit 06dd63
  sigset_t mask;
Packit 06dd63
  sigset_t oldmask;
Packit 06dd63
  int saved_errno;
Packit 06dd63
Packit 06dd63
  if (sig < 0 || NSIG <= sig || sig == SIGKILL || sig == SIGSTOP
Packit 06dd63
      || (act && act->sa_handler == SIG_ERR))
Packit 06dd63
    {
Packit 06dd63
      errno = EINVAL;
Packit 06dd63
      return -1;
Packit 06dd63
    }
Packit 06dd63
Packit 06dd63
#ifdef SIGABRT_COMPAT
Packit 06dd63
  if (sig == SIGABRT_COMPAT)
Packit 06dd63
    sig = SIGABRT;
Packit 06dd63
#endif
Packit 06dd63
Packit 06dd63
  /* POSIX requires sigaction() to be async-signal-safe.  In other
Packit 06dd63
     words, if an asynchronous signal can occur while we are anywhere
Packit 06dd63
     inside this function, the user's handler could then call
Packit 06dd63
     sigaction() recursively and expect consistent results.  We meet
Packit 06dd63
     this rule by using sigprocmask to block all signals before
Packit 06dd63
     modifying any data structure that could be read from a signal
Packit 06dd63
     handler; this works since we know that the gnulib sigprocmask
Packit 06dd63
     replacement does not try to use sigaction() from its handler.  */
Packit 06dd63
  if (!act && !oact)
Packit 06dd63
    return 0;
Packit 06dd63
  sigfillset (&mask);
Packit 06dd63
  sigprocmask (SIG_BLOCK, &mask, &oldmask);
Packit 06dd63
  if (oact)
Packit 06dd63
    {
Packit 06dd63
      if (action_array[sig].sa_handler)
Packit 06dd63
        *oact = action_array[sig];
Packit 06dd63
      else
Packit 06dd63
        {
Packit 06dd63
          /* Safe to change the handler at will here, since all
Packit 06dd63
             signals are currently blocked.  */
Packit 06dd63
          oact->sa_handler = signal (sig, SIG_DFL);
Packit 06dd63
          if (oact->sa_handler == SIG_ERR)
Packit 06dd63
            goto failure;
Packit 06dd63
          signal (sig, oact->sa_handler);
Packit 06dd63
          oact->sa_flags = SA_RESETHAND | SA_NODEFER;
Packit 06dd63
          sigemptyset (&oact->sa_mask);
Packit 06dd63
        }
Packit 06dd63
    }
Packit 06dd63
Packit 06dd63
  if (act)
Packit 06dd63
    {
Packit 06dd63
      /* Safe to install the handler before updating action_array,
Packit 06dd63
         since all signals are currently blocked.  */
Packit 06dd63
      if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN)
Packit 06dd63
        {
Packit 06dd63
          if (signal (sig, act->sa_handler) == SIG_ERR)
Packit 06dd63
            goto failure;
Packit 06dd63
          action_array[sig].sa_handler = NULL;
Packit 06dd63
        }
Packit 06dd63
      else
Packit 06dd63
        {
Packit 06dd63
          if (signal (sig, sigaction_handler) == SIG_ERR)
Packit 06dd63
            goto failure;
Packit 06dd63
          action_array[sig] = *act;
Packit 06dd63
        }
Packit 06dd63
    }
Packit 06dd63
  sigprocmask (SIG_SETMASK, &oldmask, NULL);
Packit 06dd63
  return 0;
Packit 06dd63
Packit 06dd63
 failure:
Packit 06dd63
  saved_errno = errno;
Packit 06dd63
  sigprocmask (SIG_SETMASK, &oldmask, NULL);
Packit 06dd63
  errno = saved_errno;
Packit 06dd63
  return -1;
Packit 06dd63
}