about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--signal/Makefile5
-rw-r--r--signal/tst-sigset2.c184
-rw-r--r--sysdeps/posix/sigset.c23
4 files changed, 211 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 10daff82bb..c54c3a0b31 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2006-04-23  Ulrich Drepper  <drepper@redhat.com>
+
+	[BZ #1951]
+	* sysdeps/posix/sigset.c (sigset): Return correct value reflecting
+	previous signal state.
+	* signal/Makefile (tests): Add tst-sigset2.
+	* signal/tst-sigset2.c: New file.
+
 2006-04-21  Jakub Jelinek  <jakub@redhat.com>
 
 	* argp/argp.h (__option_is_short): Check upper limit of
diff --git a/signal/Makefile b/signal/Makefile
index b5c18fef04..fa8d098da0 100644
--- a/signal/Makefile
+++ b/signal/Makefile
@@ -1,5 +1,4 @@
-# Copyright (C) 1991,1992,1993,1994,1995,1996,1997,1998,2003
-#	Free Software Foundation, Inc.
+# Copyright (C) 1991-1998,2003,2006 Free Software Foundation, Inc.
 # This file is part of the GNU C Library.
 
 # The GNU C Library is free software; you can redistribute it and/or
@@ -38,7 +37,7 @@ routines	:= signal raise killpg \
 		   allocrtsig sigtimedwait sigwaitinfo sigqueue \
 		   sighold sigrelse sigignore sigset
 
-tests		:= tst-signal tst-sigset tst-sigsimple tst-raise
+tests		:= tst-signal tst-sigset tst-sigsimple tst-raise tst-sigset2
 
 distribute	:= sigsetops.h testrtsig.h sigset-cvt-mask.h
 
diff --git a/signal/tst-sigset2.c b/signal/tst-sigset2.c
new file mode 100644
index 0000000000..f653323321
--- /dev/null
+++ b/signal/tst-sigset2.c
@@ -0,0 +1,184 @@
+/* sigset_SIG_HOLD_bug.c [BZ #1951] */
+#include <errno.h>
+#include <error.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#define TEST_SIG SIGINT
+
+
+/* Print mask of blocked signals for this process */
+static void
+printSigMask (const char *msg)
+{
+  sigset_t currMask;
+  int sig;
+  int cnt;
+
+  if (msg != NULL)
+    printf ("%s", msg);
+
+  if (sigprocmask (SIG_BLOCK, NULL, &currMask) == -1)
+    error (1, errno, "sigaction");
+
+  cnt = 0;
+  for (sig = 1; sig < NSIG; sig++)
+    {
+      if (sigismember (&currMask, sig))
+	{
+	  cnt++;
+	  printf ("\t\t%d (%s)\n", sig, strsignal (sig));
+        }
+    }
+
+  if (cnt == 0)
+    printf ("\t\t<empty signal set>\n");
+} /* printSigMask */
+
+static void
+handler (int sig)
+{
+  printf ("Caught signal %d\n", sig);
+  printSigMask ("Signal mask in handler\n");
+  printf ("Handler returning\n");
+  _exit (1);
+} /* handler */
+
+static void
+printDisposition (sighandler_t disp)
+{
+  if (disp == SIG_HOLD)
+    printf ("SIG_HOLD");
+  else if (disp == SIG_DFL)
+    printf ("SIG_DFL");
+  else if (disp == SIG_IGN)
+    printf ("SIG_IGN");
+  else
+    printf ("handled at %" PRIxPTR, (uintptr_t) disp);
+} /* printDisposition */
+
+static int
+returnTest1 (void)
+{
+  sighandler_t prev;
+
+  printf ("===== TEST 1 =====\n");
+  printf ("Blocking signal with sighold()\n");
+  if (sighold (TEST_SIG) == -1)
+    error (1, errno, "sighold");
+  printSigMask ("Signal mask after sighold()\n");
+
+  printf ("About to use sigset() to establish handler\n");
+  prev = sigset (TEST_SIG, handler);
+  if (prev == SIG_ERR)
+    error(1, errno, "sigset");
+
+  printf ("Previous disposition: ");
+  printDisposition (prev);
+  printf (" (should be SIG_HOLD)\n");
+  if (prev != SIG_HOLD)
+    {
+      printf("TEST FAILED!!!\n");
+      return 1;
+    }
+  return 0;
+} /* returnTest1 */
+
+static int
+returnTest2 (void)
+{
+  sighandler_t prev;
+
+  printf ("\n===== TEST 2 =====\n");
+
+  printf ("About to use sigset() to set SIG_HOLD\n");
+  prev = sigset (TEST_SIG, SIG_HOLD);
+  if (prev == SIG_ERR)
+    error (1, errno, "sigset");
+
+  printf ("Previous disposition: ");
+  printDisposition (prev);
+  printf (" (should be SIG_DFL)\n");
+  if (prev != SIG_DFL)
+    {
+      printf("TEST FAILED!!!\n");
+      return 1;
+    }
+  return 0;
+} /* returnTest2 */
+
+static int
+returnTest3 (void)
+{
+  sighandler_t prev;
+
+  printf ("\n===== TEST 3 =====\n");
+
+  printf ("About to use sigset() to set SIG_HOLD\n");
+  prev = sigset (TEST_SIG, SIG_HOLD);
+  if (prev == SIG_ERR)
+    error (1, errno, "sigset");
+
+  printf ("About to use sigset() to set SIG_HOLD (again)\n");
+  prev = sigset (TEST_SIG, SIG_HOLD);
+  if (prev == SIG_ERR)
+    error (1, errno, "sigset");
+
+  printf ("Previous disposition: ");
+  printDisposition (prev);
+  printf (" (should be SIG_HOLD)\n");
+  if (prev != SIG_HOLD)
+    {
+      printf("TEST FAILED!!!\n");
+      return 1;
+    }
+  return 0;
+} /* returnTest3 */
+
+int
+main (int argc, char *argv[])
+{
+  pid_t childPid;
+
+  childPid = fork();
+  if (childPid == -1)
+    error (1, errno, "fork");
+
+  if (childPid == 0)
+    exit (returnTest1 ());
+
+  int status;
+  if (TEMP_FAILURE_RETRY (waitpid (childPid, &status, 0)) != childPid)
+    error (1, errno, "waitpid");
+  int result = !WIFEXITED (status) || WEXITSTATUS (status) != 0;
+
+  childPid = fork();
+  if (childPid == -1)
+    error (1, errno, "fork");
+
+  if (childPid == 0)
+    exit (returnTest2 ());
+
+  if (TEMP_FAILURE_RETRY (waitpid (childPid, &status, 0)) != childPid)
+    error (1, errno, "waitpid");
+  result |= !WIFEXITED (status) || WEXITSTATUS (status) != 0;
+
+  childPid = fork();
+  if (childPid == -1)
+    error (1, errno, "fork");
+
+  if (childPid == 0)
+    exit (returnTest3 ());
+
+  if (TEMP_FAILURE_RETRY (waitpid (childPid, &status, 0)) != childPid)
+    error (1, errno, "waitpid");
+  result |= !WIFEXITED (status) || WEXITSTATUS (status) != 0;
+
+  return result;
+} /* main */
diff --git a/sysdeps/posix/sigset.c b/sysdeps/posix/sigset.c
index 31e39d78b5..8f96e3d610 100644
--- a/sysdeps/posix/sigset.c
+++ b/sysdeps/posix/sigset.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998, 2000, 2005 Free Software Foundation, Inc.
+/* Copyright (C) 1998, 2000, 2005, 2006 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -29,8 +29,10 @@ sigset (sig, disp)
      int sig;
      __sighandler_t disp;
 {
-  struct sigaction act, oact;
+  struct sigaction act;
+  struct sigaction oact;
   sigset_t set;
+  sigset_t oset;
 
 #ifdef SIG_HOLD
   /* Handle SIG_HOLD first.  */
@@ -45,10 +47,18 @@ sigset (sig, disp)
 	return SIG_ERR;
 
       /* Add the signal set to the current signal mask.  */
-      if (__sigprocmask (SIG_BLOCK, &set, NULL) < 0)
+      if (__sigprocmask (SIG_BLOCK, &set, &oset) < 0)
 	return SIG_ERR;
 
-      return SIG_HOLD;
+      /* If the signal was already blocked signal this to the caller.  */
+      if (__sigismember (&oset, sig))
+	return SIG_HOLD;
+
+      /* We need to determine whether a specific handler is installed.  */
+      if (__sigaction (sig, NULL, &oact) < 0)
+	return SIG_ERR;
+
+      return oact.sa_handler;
     }
 #endif	/* SIG_HOLD */
 
@@ -75,8 +85,9 @@ sigset (sig, disp)
     return SIG_ERR;
 
   /* Remove the signal set from the current signal mask.  */
-  if (__sigprocmask (SIG_UNBLOCK, &set, NULL) < 0)
+  if (__sigprocmask (SIG_UNBLOCK, &set, &oset) < 0)
     return SIG_ERR;
 
-  return oact.sa_handler;
+  /* If the signal was already blocked return SIG_HOLD.  */
+  return __sigismember (&oset, sig) ? SIG_HOLD : oact.sa_handler;
 }