|
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 |
}
|