|
Packit Service |
51e54d |
/* POSIX compatible signal blocking.
|
|
Packit Service |
51e54d |
Copyright (C) 2006-2014 Free Software Foundation, Inc.
|
|
Packit Service |
51e54d |
Written by Bruno Haible <bruno@clisp.org>, 2006.
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
This program is free software: you can redistribute it and/or modify
|
|
Packit Service |
51e54d |
it under the terms of the GNU General Public License as published by
|
|
Packit Service |
51e54d |
the Free Software Foundation; either version 3 of the License, or
|
|
Packit Service |
51e54d |
(at your option) any later version.
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
This program is distributed in the hope that it will be useful,
|
|
Packit Service |
51e54d |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
51e54d |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
51e54d |
GNU General Public License for more details.
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
You should have received a copy of the GNU General Public License
|
|
Packit Service |
51e54d |
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#include <config.h>
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Specification. */
|
|
Packit Service |
51e54d |
#include <signal.h>
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#include <errno.h>
|
|
Packit Service |
51e54d |
#include <stdint.h>
|
|
Packit Service |
51e54d |
#include <stdlib.h>
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
|
|
Packit Service |
51e54d |
# include "msvc-inval.h"
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* We assume that a platform without POSIX signal blocking functions
|
|
Packit Service |
51e54d |
also does not have the POSIX sigaction() function, only the
|
|
Packit Service |
51e54d |
signal() function. We also assume signal() has SysV semantics,
|
|
Packit Service |
51e54d |
where any handler is uninstalled prior to being invoked. This is
|
|
Packit Service |
51e54d |
true for native Windows platforms. */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* We use raw signal(), but also provide a wrapper rpl_signal() so
|
|
Packit Service |
51e54d |
that applications can query or change a blocked signal. */
|
|
Packit Service |
51e54d |
#undef signal
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Provide invalid signal numbers as fallbacks if the uncatchable
|
|
Packit Service |
51e54d |
signals are not defined. */
|
|
Packit Service |
51e54d |
#ifndef SIGKILL
|
|
Packit Service |
51e54d |
# define SIGKILL (-1)
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
#ifndef SIGSTOP
|
|
Packit Service |
51e54d |
# define SIGSTOP (-1)
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
|
|
Packit Service |
51e54d |
for the signal SIGABRT. Only one signal handler is stored for both
|
|
Packit Service |
51e54d |
SIGABRT and SIGABRT_COMPAT. SIGABRT_COMPAT is not a signal of its own. */
|
|
Packit Service |
51e54d |
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
|
|
Packit Service |
51e54d |
# undef SIGABRT_COMPAT
|
|
Packit Service |
51e54d |
# define SIGABRT_COMPAT 6
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
#ifdef SIGABRT_COMPAT
|
|
Packit Service |
51e54d |
# define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT)
|
|
Packit Service |
51e54d |
#else
|
|
Packit Service |
51e54d |
# define SIGABRT_COMPAT_MASK 0
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
typedef void (*handler_t) (int);
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#if HAVE_MSVC_INVALID_PARAMETER_HANDLER
|
|
Packit Service |
51e54d |
static handler_t
|
|
Packit Service |
51e54d |
signal_nothrow (int sig, handler_t handler)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
handler_t result;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
TRY_MSVC_INVAL
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
result = signal (sig, handler);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
CATCH_MSVC_INVAL
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
result = SIG_ERR;
|
|
Packit Service |
51e54d |
errno = EINVAL;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
DONE_MSVC_INVAL;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return result;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
# define signal signal_nothrow
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Handling of gnulib defined signals. */
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#if GNULIB_defined_SIGPIPE
|
|
Packit Service |
51e54d |
static handler_t SIGPIPE_handler = SIG_DFL;
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#if GNULIB_defined_SIGPIPE
|
|
Packit Service |
51e54d |
static handler_t
|
|
Packit Service |
51e54d |
ext_signal (int sig, handler_t handler)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
switch (sig)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
case SIGPIPE:
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
handler_t old_handler = SIGPIPE_handler;
|
|
Packit Service |
51e54d |
SIGPIPE_handler = handler;
|
|
Packit Service |
51e54d |
return old_handler;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
default: /* System defined signal */
|
|
Packit Service |
51e54d |
return signal (sig, handler);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
# undef signal
|
|
Packit Service |
51e54d |
# define signal ext_signal
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
sigismember (const sigset_t *set, int sig)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (sig >= 0 && sig < NSIG)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
#ifdef SIGABRT_COMPAT
|
|
Packit Service |
51e54d |
if (sig == SIGABRT_COMPAT)
|
|
Packit Service |
51e54d |
sig = SIGABRT;
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
return (*set >> sig) & 1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
sigemptyset (sigset_t *set)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
*set = 0;
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
sigaddset (sigset_t *set, int sig)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (sig >= 0 && sig < NSIG)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
#ifdef SIGABRT_COMPAT
|
|
Packit Service |
51e54d |
if (sig == SIGABRT_COMPAT)
|
|
Packit Service |
51e54d |
sig = SIGABRT;
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
*set |= 1U << sig;
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
errno = EINVAL;
|
|
Packit Service |
51e54d |
return -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
sigdelset (sigset_t *set, int sig)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (sig >= 0 && sig < NSIG)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
#ifdef SIGABRT_COMPAT
|
|
Packit Service |
51e54d |
if (sig == SIGABRT_COMPAT)
|
|
Packit Service |
51e54d |
sig = SIGABRT;
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
*set &= ~(1U << sig);
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
errno = EINVAL;
|
|
Packit Service |
51e54d |
return -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
sigfillset (sigset_t *set)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
*set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Set of currently blocked signals. */
|
|
Packit Service |
51e54d |
static volatile sigset_t blocked_set /* = 0 */;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Set of currently blocked and pending signals. */
|
|
Packit Service |
51e54d |
static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Signal handler that is installed for blocked signals. */
|
|
Packit Service |
51e54d |
static void
|
|
Packit Service |
51e54d |
blocked_handler (int sig)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
/* Reinstall the handler, in case the signal occurs multiple times
|
|
Packit Service |
51e54d |
while blocked. There is an inherent race where an asynchronous
|
|
Packit Service |
51e54d |
signal in between when the kernel uninstalled the handler and
|
|
Packit Service |
51e54d |
when we reinstall it will trigger the default handler; oh
|
|
Packit Service |
51e54d |
well. */
|
|
Packit Service |
51e54d |
signal (sig, blocked_handler);
|
|
Packit Service |
51e54d |
if (sig >= 0 && sig < NSIG)
|
|
Packit Service |
51e54d |
pending_array[sig] = 1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
sigpending (sigset_t *set)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
sigset_t pending = 0;
|
|
Packit Service |
51e54d |
int sig;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (sig = 0; sig < NSIG; sig++)
|
|
Packit Service |
51e54d |
if (pending_array[sig])
|
|
Packit Service |
51e54d |
pending |= 1U << sig;
|
|
Packit Service |
51e54d |
*set = pending;
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* The previous signal handlers.
|
|
Packit Service |
51e54d |
Only the array elements corresponding to blocked signals are relevant. */
|
|
Packit Service |
51e54d |
static volatile handler_t old_handlers[NSIG];
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (old_set != NULL)
|
|
Packit Service |
51e54d |
*old_set = blocked_set;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (set != NULL)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
sigset_t new_blocked_set;
|
|
Packit Service |
51e54d |
sigset_t to_unblock;
|
|
Packit Service |
51e54d |
sigset_t to_block;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
switch (operation)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
case SIG_BLOCK:
|
|
Packit Service |
51e54d |
new_blocked_set = blocked_set | *set;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
case SIG_SETMASK:
|
|
Packit Service |
51e54d |
new_blocked_set = *set;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
case SIG_UNBLOCK:
|
|
Packit Service |
51e54d |
new_blocked_set = blocked_set & ~*set;
|
|
Packit Service |
51e54d |
break;
|
|
Packit Service |
51e54d |
default:
|
|
Packit Service |
51e54d |
errno = EINVAL;
|
|
Packit Service |
51e54d |
return -1;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
to_unblock = blocked_set & ~new_blocked_set;
|
|
Packit Service |
51e54d |
to_block = new_blocked_set & ~blocked_set;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (to_block != 0)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
int sig;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (sig = 0; sig < NSIG; sig++)
|
|
Packit Service |
51e54d |
if ((to_block >> sig) & 1)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
pending_array[sig] = 0;
|
|
Packit Service |
51e54d |
if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
|
|
Packit Service |
51e54d |
blocked_set |= 1U << sig;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (to_unblock != 0)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
sig_atomic_t received[NSIG];
|
|
Packit Service |
51e54d |
int sig;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (sig = 0; sig < NSIG; sig++)
|
|
Packit Service |
51e54d |
if ((to_unblock >> sig) & 1)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (signal (sig, old_handlers[sig]) != blocked_handler)
|
|
Packit Service |
51e54d |
/* The application changed a signal handler while the signal
|
|
Packit Service |
51e54d |
was blocked, bypassing our rpl_signal replacement.
|
|
Packit Service |
51e54d |
We don't support this. */
|
|
Packit Service |
51e54d |
abort ();
|
|
Packit Service |
51e54d |
received[sig] = pending_array[sig];
|
|
Packit Service |
51e54d |
blocked_set &= ~(1U << sig);
|
|
Packit Service |
51e54d |
pending_array[sig] = 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
received[sig] = 0;
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
for (sig = 0; sig < NSIG; sig++)
|
|
Packit Service |
51e54d |
if (received[sig])
|
|
Packit Service |
51e54d |
raise (sig);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
/* Install the handler FUNC for signal SIG, and return the previous
|
|
Packit Service |
51e54d |
handler. */
|
|
Packit Service |
51e54d |
handler_t
|
|
Packit Service |
51e54d |
rpl_signal (int sig, handler_t handler)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
/* We must provide a wrapper, so that a user can query what handler
|
|
Packit Service |
51e54d |
they installed even if that signal is currently blocked. */
|
|
Packit Service |
51e54d |
if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
|
|
Packit Service |
51e54d |
&& handler != SIG_ERR)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
#ifdef SIGABRT_COMPAT
|
|
Packit Service |
51e54d |
if (sig == SIGABRT_COMPAT)
|
|
Packit Service |
51e54d |
sig = SIGABRT;
|
|
Packit Service |
51e54d |
#endif
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
if (blocked_set & (1U << sig))
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
/* POSIX states that sigprocmask and signal are both
|
|
Packit Service |
51e54d |
async-signal-safe. This is not true of our
|
|
Packit Service |
51e54d |
implementation - there is a slight data race where an
|
|
Packit Service |
51e54d |
asynchronous interrupt on signal A can occur after we
|
|
Packit Service |
51e54d |
install blocked_handler but before we have updated
|
|
Packit Service |
51e54d |
old_handlers for signal B, such that handler A can see
|
|
Packit Service |
51e54d |
stale information if it calls signal(B). Oh well -
|
|
Packit Service |
51e54d |
signal handlers really shouldn't try to manipulate the
|
|
Packit Service |
51e54d |
installed handlers of unrelated signals. */
|
|
Packit Service |
51e54d |
handler_t result = old_handlers[sig];
|
|
Packit Service |
51e54d |
old_handlers[sig] = handler;
|
|
Packit Service |
51e54d |
return result;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
return signal (sig, handler);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
errno = EINVAL;
|
|
Packit Service |
51e54d |
return SIG_ERR;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
|
|
Packit Service |
51e54d |
#if GNULIB_defined_SIGPIPE
|
|
Packit Service |
51e54d |
/* Raise the signal SIGPIPE. */
|
|
Packit Service |
51e54d |
int
|
|
Packit Service |
51e54d |
_gl_raise_SIGPIPE (void)
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
if (blocked_set & (1U << SIGPIPE))
|
|
Packit Service |
51e54d |
pending_array[SIGPIPE] = 1;
|
|
Packit Service |
51e54d |
else
|
|
Packit Service |
51e54d |
{
|
|
Packit Service |
51e54d |
handler_t handler = SIGPIPE_handler;
|
|
Packit Service |
51e54d |
if (handler == SIG_DFL)
|
|
Packit Service |
51e54d |
exit (128 + SIGPIPE);
|
|
Packit Service |
51e54d |
else if (handler != SIG_IGN)
|
|
Packit Service |
51e54d |
(*handler) (SIGPIPE);
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
return 0;
|
|
Packit Service |
51e54d |
}
|
|
Packit Service |
51e54d |
#endif
|