* modules/sigaction: New module...
* modules/sigaction-tests: ...and its test.
* m4/sigaction.m4: New file.
* lib/sigaction.c: Likewise.
* tests/test-sigaction.c: Likewise.
* m4/signal_h.m4 (gl_SIGNAL_H_DEFAULTS): Add sigaction variables.
* modules/signal (Makefile.am): Likewise.
* lib/signal.in.h (!@HAVE_SIGACTION@): Define replacements when
needed.
* doc/posix-headers/signal.texi (signal.h): Mention provided
types.
* doc/posix-functions/siginterrupt.texi (siginterrupt): Mention
that sigaction is preferable.
* doc/posix-functions/sigaction.texi (sigaction): Mention new
module.
* MODULES.html.sh (Support for systems lacking POSIX:2001): Add
sigaction.
Signed-off-by: Eric Blake <ebb9@byu.net>
2008-06-21 Eric Blake <ebb9@byu.net>
+ New module sigaction, for mingw.
+ * modules/sigaction: New module...
+ * modules/sigaction-tests: ...and its test.
+ * m4/sigaction.m4: New file.
+ * lib/sigaction.c: Likewise.
+ * tests/test-sigaction.c: Likewise.
+ * m4/signal_h.m4 (gl_SIGNAL_H_DEFAULTS): Add sigaction variables.
+ * modules/signal (Makefile.am): Likewise.
+ * lib/signal.in.h (!@HAVE_SIGACTION@): Define replacements when
+ needed.
+ * doc/posix-headers/signal.texi (signal.h): Mention provided
+ types.
+ * doc/posix-functions/siginterrupt.texi (siginterrupt): Mention
+ that sigaction is preferable.
+ * doc/posix-functions/sigaction.texi (sigaction): Mention new
+ module.
+ * MODULES.html.sh (Support for systems lacking POSIX:2001): Add
+ sigaction.
+
Improve robustness of sigprocmask by overriding signal.
* lib/signal.in.h (rpl_signal): Override signal when sigprocmask
is in use.
func_module rename
func_module rmdir
func_module search
+ func_module sigaction
func_module sigprocmask
func_module socklen
func_module ssize_t
POSIX specification: @url{http://www.opengroup.org/susv3xsh/sigaction.html}
-Gnulib module: ---
+Gnulib module: sigaction
Portability problems fixed by Gnulib:
@itemize
+@item
+This function is missing on some platforms:
+mingw.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
@item
-This function is missing on some platforms:
+POSIX recommends that when specifying SA_RESETHAND, SA_NODEFER must
+also be specified.
+
+@item
+Support for SA_ONSTACK is missing on some platforms:
+mingw, cygwin.
+
+@item
+Support for SA_SIGINFO is missing on some platforms:
+mingw, Interix 3.5.
+
+@item
+Support for SIGCHLD, and thus for SA_NOCLDSTOP and SA_NOCLDWAIT, is
+missing on some platforms:
mingw.
+
+@item
+Support for SA_RESTART is missing on some platforms:
+mingw.
+
@item
The symbolic value @code{SIG_IGN} for the @code{SIGCHLD} signal is equivalent
to a signal handler
@item
This function is missing on some platforms:
Solaris 2.5.1, mingw, Interix 3.5, BeOS.
+
+@item
+POSIX recommends using @code{sigaction} with SA_NODEFER instead.
@end itemize
POSIX specification: @url{http://www.opengroup.org/susv3xbd/signal.h.html}
-Gnulib module: ---
+Gnulib module: signal
Portability problems fixed by Gnulib:
@itemize
+@item
+@code{sigset_t} is only declared in <sys/types.h> on some platforms:
+mingw.
+
+@item
+@code{struct sigaction} and @code{siginfo_t} are missing on some
+platforms:
+mingw.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
+@item
+@code{struct sigaction} lacks the @code{sa_sigaction} member on some
+platforms; this can also be detected by whether @code{SA_SIGINFO} is defined:
+Interix 3.5.
@end itemize
--- /dev/null
+/* POSIX compatible signal blocking.
+ Copyright (C) 2008 Free Software Foundation, Inc.
+ Written by Eric Blake <ebb9@byu.net>, 2008.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <signal.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/* This implementation of sigaction is tailored to Woe32 behavior:
+ signal() has SysV semantics (ie. the handler is uninstalled before
+ it is invoked). This is an inherent data race if an asynchronous
+ signal is sent twice in a row before we can reinstall our handler,
+ but there's nothing we can do about it. Meanwhile, sigprocmask()
+ is not present, and while we can use the gnulib replacement to
+ provide critical sections, it too suffers from potential data races
+ in the face of an ill-timed asynchronous signal. And we compound
+ the situation by reading static storage in a signal handler, which
+ POSIX warns is not generically async-signal-safe. Oh well.
+
+ Additionally, SIGCHLD is not defined, so we don't implement
+ SA_NOCLDSTOP or SA_NOCLDWAIT; sigaltstack() is not present, so we
+ don't implement SA_ONSTACK; and siginterrupt() is not present, so
+ we don't implement SA_RESTART. Supporting SA_SIGINFO is impossible
+ to do portably.
+
+ POSIX states that an application should not mix signal() and
+ sigaction(). We support the use of signal() within the gnulib
+ sigprocmask() substitute, but all other application code linked
+ with this module should stick with only sigaction(). */
+
+/* Check some of our assumptions. */
+#if defined SIGCHLD || defined HAVE_SIGALTSTACK || defined HAVE_SIGINTERRUPT
+# error "Revisit the assumptions made in the sigaction module"
+#endif
+
+/* Out-of-range substitutes make a good fallback for uncatchable
+ signals. */
+#ifndef SIGKILL
+# define SIGKILL (-1)
+#endif
+#ifndef SIGSTOP
+# define SIGSTOP (-1)
+#endif
+
+/* A signal handler. */
+typedef void (*handler_t) (int signal);
+
+/* Set of current actions. If sa_handler for an entry is NULL, then
+ that signal is not currently handled by the sigaction handler. */
+static struct sigaction volatile action_array[NSIG] /* = 0 */;
+
+/* Signal handler that is installed for signals. */
+static void
+sigaction_handler (int sig)
+{
+ handler_t handler;
+ sigset_t mask;
+ sigset_t oldmask;
+ int saved_errno = errno;
+ if (sig < 0 || NSIG <= sig || !action_array[sig].sa_handler)
+ {
+ /* Unexpected situation; be careful to avoid recursive abort. */
+ if (sig == SIGABRT)
+ signal (SIGABRT, SIG_DFL);
+ abort ();
+ }
+
+ /* Reinstall the signal handler when required; otherwise update the
+ bookkeeping so that the user's handler may call sigaction and get
+ accurate results. We know the signal isn't currently blocked, or
+ we wouldn't be in its handler, therefore we know that we are not
+ interrupting a sigaction() call. There is a race where any
+ asynchronous instance of the same signal occurring before we
+ reinstall the handler will trigger the default handler; oh
+ well. */
+ handler = action_array[sig].sa_handler;
+ if ((action_array[sig].sa_flags & SA_RESETHAND) == 0)
+ signal (sig, sigaction_handler);
+ else
+ action_array[sig].sa_handler = NULL;
+
+ /* Block appropriate signals. */
+ mask = action_array[sig].sa_mask;
+ if ((action_array[sig].sa_flags & SA_NODEFER) == 0)
+ sigaddset (&mask, sig);
+ sigprocmask (SIG_BLOCK, &mask, &oldmask);
+
+ /* Invoke the user's handler, then restore prior mask. */
+ errno = saved_errno;
+ handler (sig);
+ saved_errno = errno;
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ errno = saved_errno;
+}
+
+/* Change and/or query the action that will be taken on delivery of
+ signal SIG. If not NULL, ACT describes the new behavior. If not
+ NULL, OACT is set to the prior behavior. Return 0 on success, or
+ set errno and return -1 on failure. */
+int
+sigaction (int sig, const struct sigaction *restrict act,
+ struct sigaction *restrict oact)
+{
+ sigset_t mask;
+ sigset_t oldmask;
+ int saved_errno;
+
+ if (sig < 0 || NSIG <= sig || sig == SIGKILL || sig == SIGSTOP
+ || (act && act->sa_handler == SIG_ERR))
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* POSIX requires sigaction() to be async-signal-safe. In other
+ words, if an asynchronous signal can occur while we are anywhere
+ inside this function, the user's handler could then call
+ sigaction() recursively and expect consistent results. We meet
+ this rule by using sigprocmask to block all signals before
+ modifying any data structure that could be read from a signal
+ handler; this works since we know that the gnulib sigprocmask
+ replacement does not try to use sigaction() from its handler. */
+ if (!act && !oact)
+ return 0;
+ sigfillset (&mask);
+ sigprocmask (SIG_BLOCK, &mask, &oldmask);
+ if (oact)
+ {
+ if (action_array[sig].sa_handler)
+ *oact = action_array[sig];
+ else
+ {
+ /* Safe to change the handler at will here, since all
+ signals are currently blocked. */
+ oact->sa_handler = signal (sig, SIG_DFL);
+ if (oact->sa_handler == SIG_ERR)
+ goto failure;
+ signal (sig, oact->sa_handler);
+ oact->sa_flags = SA_RESETHAND | SA_NODEFER;
+ sigemptyset (&oact->sa_mask);
+ }
+ }
+
+ if (act)
+ {
+ /* Safe to install the handler before updating action_array,
+ since all signals are currently blocked. */
+ if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN)
+ {
+ if (signal (sig, act->sa_handler) == SIG_ERR)
+ goto failure;
+ action_array[sig].sa_handler = NULL;
+ }
+ else
+ {
+ if (signal (sig, sigaction_handler) == SIG_ERR)
+ goto failure;
+ action_array[sig] = *act;
+ }
+ }
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ return 0;
+
+ failure:
+ saved_errno = errno;
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ errno = saved_errno;
+ return -1;
+}
/* The definition of GL_LINK_WARNING is copied here. */
/* Mingw defines sigset_t not in <signal.h>, but in <sys/types.h>. */
-#if !@HAVE_POSIX_SIGNALBLOCKING@
-# include <sys/types.h>
-#endif
+#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
handler. */
extern void (*signal (int sig, void (*func) (int))) (int);
-#endif
+#endif /* !@HAVE_POSIX_SIGNALBLOCKING@ */
+
+#if !@HAVE_SIGACTION@
+
+# if !@HAVE_SIGINFO_T@
+/* Present to allow compilation, but unsupported by gnulib. */
+union sigval
+{
+ int sival_int;
+ void *sival_ptr;
+};
+
+/* Present to allow compilation, but unsupported by gnulib. */
+struct siginfo_t
+{
+ int si_signo;
+ int si_code;
+ int si_errno;
+ pid_t si_pid;
+ uid_t si_uid;
+ void *si_addr;
+ int si_status;
+ long si_band;
+ union sigval si_value;
+};
+typedef struct siginfo_t siginfo_t;
+# endif /* !@HAVE_SIGINFO_T@ */
+
+ /* Due to autoconf conventions, we can't tell if HAVE_SIGACTION
+ means we have the type or means we have the function. We assume
+ that all implementations either have both or neither. */
+
+struct sigaction
+{
+ union
+ {
+ void (*_sa_handler) (int);
+ /* Present to allow compilation, but unsupported by gnulib. POSIX
+ says that implementations may, but not must, make sa_sigaction
+ overlap with sa_handler, but we know of no implementation where
+ they do not overlap. */
+ void (*_sa_sigaction) (int, siginfo_t *, void *);
+ } _sa_func;
+ sigset_t sa_mask;
+ /* Not all POSIX flags are supported. */
+ int sa_flags;
+};
+# define sa_handler _sa_func._sa_handler
+# define sa_sigaction _sa_func._sa_sigaction
+/* Unsupported flags are not present. */
+# define SA_RESETHAND 1
+# define SA_NODEFER 2
+
+extern int sigaction (int, const struct sigaction *restrict,
+ struct sigaction *restrict);
+
+#endif /* !@HAVE_SIGACTION@ */
#ifdef __cplusplus
--- /dev/null
+# sigaction.m4 serial 1
+dnl Copyright (C) 2008 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Determine if sigaction interface is present.
+AC_DEFUN([gl_SIGACTION],
+[
+ dnl Due to autoconf conventions, we can't tell if HAVE_SIGACTION
+ dnl means we have the type or means we have the function. We assume
+ dnl that all implementations either have both or neither.
+ AC_REPLACE_FUNCS([sigaction])
+ if test $ac_cv_func_sigaction = no ; then
+ HAVE_SIGACTION=0
+ AC_SUBST([HAVE_SIGACTION])
+ gl_PREREQ_SIGACTION
+ fi
+])
+
+# Prerequisites of the part of lib/signal.in.h and of lib/sigprocmask.c.
+AC_DEFUN([gl_PREREQ_SIGACTION],
+[
+ AC_REQUIRE([AC_C_RESTRICT])
+ AC_REQUIRE([AC_TYPE_UID_T])
+ AC_REQUIRE([gl_SIGNAL_H_DEFAULTS])
+ AC_CHECK_FUNCS_ONCE([sigaltstack siginterrupt])
+ AC_CHECK_TYPES([siginfo_t], [], [], [[
+#include <signal.h>
+ ]])
+ if test $ac_cv_type_siginfo_t = no; then
+ HAVE_SIGINFO_T=0
+ AC_SUBST([HAVE_SIGINFO_T])
+ fi
+])
-# signal_h.m4 serial 3
-dnl Copyright (C) 2007 Free Software Foundation, Inc.
+# signal_h.m4 serial 4
+dnl Copyright (C) 2007, 2008 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.
AC_DEFUN([gl_SIGNAL_H_DEFAULTS],
[
GNULIB_SIGPROCMASK=0; AC_SUBST([GNULIB_SIGPROCMASK])
+ GNULIB_SIGACTION=0; AC_SUBST([GNULIB_SIGACTION])
dnl Assume proper GNU behavior unless another module says otherwise.
HAVE_POSIX_SIGNALBLOCKING=1; AC_SUBST([HAVE_POSIX_SIGNALBLOCKING])
HAVE_SIGSET_T=1; AC_SUBST([HAVE_SIGSET_T])
+ HAVE_SIGINFO_T=1; AC_SUBST([HAVE_SIGINFO_T])
+ HAVE_SIGACTION=1; AC_SUBST([HAVE_SIGACTION])
])
--- /dev/null
+Description:
+POSIX compatible signal handlers.
+
+Files:
+lib/sigaction.c
+m4/sigaction.m4
+
+Depends-on:
+signal
+sigprocmask
+
+configure.ac:
+gl_SIGACTION
+gl_SIGNAL_MODULE_INDICATOR([sigaction])
+
+Makefile.am:
+
+Include:
+<signal.h>
+
+License:
+LGPL
+
+Maintainer:
+Eric Blake
--- /dev/null
+Files:
+tests/test-sigaction.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-sigaction
+check_PROGRAMS += test-sigaction
sed -e 's/@''INCLUDE_NEXT''@/$(INCLUDE_NEXT)/g' \
-e 's|@''NEXT_SIGNAL_H''@|$(NEXT_SIGNAL_H)|g' \
-e 's|@''GNULIB_SIGPROCMASK''@|$(GNULIB_SIGPROCMASK)|g' \
+ -e 's|@''GNULIB_SIGACTION''@|$(GNULIB_SIGACTION)|g' \
-e 's|@''HAVE_POSIX_SIGNALBLOCKING''@|$(HAVE_POSIX_SIGNALBLOCKING)|g' \
-e 's|@''HAVE_SIGSET_T''@|$(HAVE_SIGSET_T)|g' \
+ -e 's|@''HAVE_SIGINFO_T''@|$(HAVE_SIGINFO_T)|g' \
+ -e 's|@''HAVE_SIGACTION''@|$(HAVE_SIGACTION)|g' \
-e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \
< $(srcdir)/signal.in.h; \
} > $@-t
--- /dev/null
+/* Test of sigaction() function.
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Eric Blake <ebb9@byu.net>, 2008. */
+
+#include <config.h>
+
+#include <signal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define ASSERT(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ { \
+ fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+ fflush (stderr); \
+ signal (SIGABRT, SIG_DFL); \
+ abort (); \
+ } \
+ } \
+ while (0)
+
+#ifndef SA_SIGINFO
+# define SA_SIGINFO 0
+#endif
+
+/* This test is unsafe in the presence of an asynchronous SIGABRT,
+ because we install a signal-handler that is intentionally not
+ async-safe. Hopefully, this does not lead to too many reports of
+ false failures, since people don't generally use 'kill -s SIGABRT'
+ to end a runaway program. */
+
+static void
+handler (int sig)
+{
+ static int entry_count;
+ struct sigaction sa;
+ ASSERT (sig == SIGABRT);
+ ASSERT (sigaction (SIGABRT, NULL, &sa) == 0);
+ ASSERT ((sa.sa_flags & SA_SIGINFO) == 0);
+ switch (entry_count++)
+ {
+ case 0:
+ ASSERT ((sa.sa_flags & SA_RESETHAND) == 0);
+ ASSERT (sa.sa_handler == handler);
+ break;
+ case 1:
+ ASSERT (sa.sa_handler == SIG_DFL);
+ break;
+ default:
+ ASSERT (0);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ struct sigaction sa;
+ struct sigaction old_sa;
+ sa.sa_handler = handler;
+ sa.sa_flags = 0;
+ ASSERT (sigemptyset (&sa.sa_mask) == 0);
+ ASSERT (sigaction (SIGABRT, &sa, NULL) == 0);
+ ASSERT (raise (SIGABRT) == 0);
+ sa.sa_flags = SA_RESETHAND | SA_NODEFER;
+ ASSERT (sigaction (SIGABRT, &sa, &old_sa) == 0);
+ ASSERT (old_sa.sa_flags == 0);
+ ASSERT (old_sa.sa_handler == handler);
+ ASSERT (raise (SIGABRT) == 0);
+ sa.sa_handler = SIG_DFL;
+ ASSERT (sigaction (SIGABRT, &sa, &old_sa) == 0);
+ ASSERT ((old_sa.sa_flags & SA_SIGINFO) == 0);
+ ASSERT (old_sa.sa_handler == SIG_DFL);
+ sa.sa_handler = SIG_IGN;
+ ASSERT (sigaction (SIGABRT, &sa, NULL) == 0);
+ ASSERT (raise (SIGABRT) == 0);
+ ASSERT (sigaction (SIGABRT, NULL, &old_sa) == 0);
+ ASSERT (old_sa.sa_handler == SIG_IGN);
+ ASSERT (raise (SIGABRT) == 0);
+ return 0;
+}