Blame lib/sigaction.c

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