New module sigaction, for mingw.
authorEric Blake <ebb9@byu.net>
Sat, 21 Jun 2008 20:32:55 +0000 (14:32 -0600)
committerEric Blake <ebb9@byu.net>
Sat, 21 Jun 2008 20:51:56 +0000 (14:51 -0600)
* 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>
13 files changed:
ChangeLog
MODULES.html.sh
doc/posix-functions/sigaction.texi
doc/posix-functions/siginterrupt.texi
doc/posix-headers/signal.texi
lib/sigaction.c [new file with mode: 0644]
lib/signal.in.h
m4/sigaction.m4 [new file with mode: 0644]
m4/signal_h.m4
modules/sigaction [new file with mode: 0644]
modules/sigaction-tests [new file with mode: 0644]
modules/signal
tests/test-sigaction.c [new file with mode: 0644]

index 7d08ae6..e3b0279 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
 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.
index f82e957..8f3aa4f 100755 (executable)
@@ -2131,6 +2131,7 @@ func_all_modules ()
   func_module rename
   func_module rmdir
   func_module search
+  func_module sigaction
   func_module sigprocmask
   func_module socklen
   func_module ssize_t
index 80e3b2c..56795d9 100644 (file)
@@ -4,17 +4,38 @@
 
 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
index 48223ab..68ac3c5 100644 (file)
@@ -15,4 +15,7 @@ Portability problems not fixed by Gnulib:
 @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
index 272e646..cb4f580 100644 (file)
@@ -3,12 +3,24 @@
 
 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
diff --git a/lib/sigaction.c b/lib/sigaction.c
new file mode 100644 (file)
index 0000000..9b0c92f
--- /dev/null
@@ -0,0 +1,187 @@
+/* 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;
+}
index eb16afb..30fa929 100644 (file)
@@ -34,9 +34,7 @@
 /* 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" {
@@ -91,7 +89,63 @@ extern int sigprocmask (int operation, const sigset_t *set, sigset_t *old_set);
    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
diff --git a/m4/sigaction.m4 b/m4/sigaction.m4
new file mode 100644 (file)
index 0000000..2f1a4cd
--- /dev/null
@@ -0,0 +1,35 @@
+# 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
+])
index 37ebca9..bf2bad4 100644 (file)
@@ -1,5 +1,5 @@
-# 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.
@@ -20,7 +20,10 @@ AC_DEFUN([gl_SIGNAL_MODULE_INDICATOR],
 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])
 ])
diff --git a/modules/sigaction b/modules/sigaction
new file mode 100644 (file)
index 0000000..60c0e8c
--- /dev/null
@@ -0,0 +1,25 @@
+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
diff --git a/modules/sigaction-tests b/modules/sigaction-tests
new file mode 100644 (file)
index 0000000..5084e9e
--- /dev/null
@@ -0,0 +1,10 @@
+Files:
+tests/test-sigaction.c
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-sigaction
+check_PROGRAMS += test-sigaction
index c96de90..afe6ab3 100644 (file)
@@ -23,8 +23,11 @@ signal.h: signal.in.h
          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
diff --git a/tests/test-sigaction.c b/tests/test-sigaction.c
new file mode 100644 (file)
index 0000000..81abe89
--- /dev/null
@@ -0,0 +1,97 @@
+/* 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;
+}