Blame gnulib/lib/sigaction.c

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