Home | History | Annotate | Line # | Download | only in gnulib-lib
      1 /* POSIX compatible signal blocking.
      2    Copyright (C) 2006 Free Software Foundation, Inc.
      3    Written by Bruno Haible <bruno (at) clisp.org>, 2006.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 2, or (at your option)
      8    any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     18 
     19 #include <config.h>
     20 
     21 /* Specification.  */
     22 #include "sigprocmask.h"
     23 
     24 #include <errno.h>
     25 #include <stdint.h>
     26 #include <stdlib.h>
     27 
     28 /* We assume that a platform without POSIX signal blocking functions also
     29    does not have the POSIX sigaction() function, only the signal() function.
     30    This is true for Woe32 platforms.  */
     31 
     32 /* A signal handler.  */
     33 typedef void (*handler_t) (int signal);
     34 
     35 int
     36 sigismember (const sigset_t *set, int sig)
     37 {
     38   if (sig >= 0 && sig < NSIG)
     39     return (*set >> sig) & 1;
     40   else
     41     return 0;
     42 }
     43 
     44 int
     45 sigemptyset (sigset_t *set)
     46 {
     47   *set = 0;
     48   return 0;
     49 }
     50 
     51 int
     52 sigaddset (sigset_t *set, int sig)
     53 {
     54   if (sig >= 0 && sig < NSIG)
     55     {
     56       *set |= 1U << sig;
     57       return 0;
     58     }
     59   else
     60     {
     61       errno = EINVAL;
     62       return -1;
     63     }
     64 }
     65 
     66 int
     67 sigdelset (sigset_t *set, int sig)
     68 {
     69   if (sig >= 0 && sig < NSIG)
     70     {
     71       *set &= ~(1U << sig);
     72       return 0;
     73     }
     74   else
     75     {
     76       errno = EINVAL;
     77       return -1;
     78     }
     79 }
     80 
     81 int
     82 sigfillset (sigset_t *set)
     83 {
     84   *set = (2U << (NSIG - 1)) - 1;
     85   return 0;
     86 }
     87 
     88 /* Set of currently blocked signals.  */
     89 static sigset_t blocked_set /* = 0 */;
     90 
     91 /* Set of currently blocked and pending signals.  */
     92 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
     93 
     94 /* Signal handler that is installed for blocked signals.  */
     95 static void
     96 blocked_handler (int sig)
     97 {
     98   if (sig >= 0 && sig < NSIG)
     99     pending_array[sig] = 1;
    100 }
    101 
    102 int
    103 sigpending (sigset_t *set)
    104 {
    105   sigset_t pending = 0;
    106   int sig;
    107 
    108   for (sig = 0; sig < NSIG; sig++)
    109     if (pending_array[sig])
    110       pending |= 1U << sig;
    111   return pending;
    112 }
    113 
    114 /* The previous signal handlers.
    115    Only the array elements corresponding to blocked signals are relevant.  */
    116 static handler_t old_handlers[NSIG];
    117 
    118 int
    119 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
    120 {
    121   if (old_set != NULL)
    122     *old_set = blocked_set;
    123 
    124   if (set != NULL)
    125     {
    126       sigset_t new_blocked_set;
    127       sigset_t to_unblock;
    128       sigset_t to_block;
    129 
    130       switch (operation)
    131 	{
    132 	case SIG_BLOCK:
    133 	  new_blocked_set = blocked_set | *set;
    134 	  break;
    135 	case SIG_SETMASK:
    136 	  new_blocked_set = *set;
    137 	  break;
    138 	case SIG_UNBLOCK:
    139 	  new_blocked_set = blocked_set & ~*set;
    140 	  break;
    141 	default:
    142 	  errno = EINVAL;
    143 	  return -1;
    144 	}
    145       to_unblock = blocked_set & ~new_blocked_set;
    146       to_block = new_blocked_set & ~blocked_set;
    147 
    148       if (to_block != 0)
    149 	{
    150 	  int sig;
    151 
    152 	  for (sig = 0; sig < NSIG; sig++)
    153 	    if ((to_block >> sig) & 1)
    154 	      {
    155 		pending_array[sig] = 0;
    156 		if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
    157 		  blocked_set |= 1U << sig;
    158 	      }
    159 	}
    160 
    161       if (to_unblock != 0)
    162 	{
    163 	  sig_atomic_t received[NSIG];
    164 	  int sig;
    165 
    166 	  for (sig = 0; sig < NSIG; sig++)
    167 	    if ((to_unblock >> sig) & 1)
    168 	      {
    169 		if (signal (sig, old_handlers[sig]) != blocked_handler)
    170 		  /* The application changed a signal handler while the signal
    171 		     was blocked.  We don't support this.  */
    172 		  abort ();
    173 		received[sig] = pending_array[sig];
    174 		blocked_set &= ~(1U << sig);
    175 		pending_array[sig] = 0;
    176 	      }
    177 	    else
    178 	      received[sig] = 0;
    179 
    180 	  for (sig = 0; sig < NSIG; sig++)
    181 	    if (received[sig])
    182 	      {
    183 		#if HAVE_RAISE
    184 		raise (sig);
    185 		#else
    186 		kill (getpid (), sig);
    187 		#endif
    188 	      }
    189 	}
    190     }
    191   return 0;
    192 }
    193