Blame gettext-tools/gnulib-lib/sigaction.c

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