Blame lib/sigaction.c

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