File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libiconv / srclib / sigprocmask.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 22:57:48 2012 UTC (12 years, 4 months ago) by misho
Branches: libiconv, MAIN
CVS tags: v1_13_1, HEAD
libiconv

    1: /* POSIX compatible signal blocking.
    2:    Copyright (C) 2006-2008 Free Software Foundation, Inc.
    3:    Written by Bruno Haible <bruno@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 3 of the License, or
    8:    (at your option) 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, see <http://www.gnu.org/licenses/>.  */
   17: 
   18: #include <config.h>
   19: 
   20: /* Specification.  */
   21: #include <signal.h>
   22: 
   23: #include <errno.h>
   24: #include <stdint.h>
   25: #include <stdlib.h>
   26: 
   27: /* We assume that a platform without POSIX signal blocking functions
   28:    also does not have the POSIX sigaction() function, only the
   29:    signal() function.  We also assume signal() has SysV semantics,
   30:    where any handler is uninstalled prior to being invoked.  This is
   31:    true for Woe32 platforms.  */
   32: 
   33: /* We use raw signal(), but also provide a wrapper rpl_signal() so
   34:    that applications can query or change a blocked signal.  */
   35: #undef signal
   36: 
   37: /* Provide invalid signal numbers as fallbacks if the uncatchable
   38:    signals are not defined.  */
   39: #ifndef SIGKILL
   40: # define SIGKILL (-1)
   41: #endif
   42: #ifndef SIGSTOP
   43: # define SIGSTOP (-1)
   44: #endif
   45: 
   46: /* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
   47:    for the signal SIGABRT.  Only one signal handler is stored for both
   48:    SIGABRT and SIGABRT_COMPAT.  SIGABRT_COMPAT is not a signal of its own.  */
   49: #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
   50: # undef SIGABRT_COMPAT
   51: # define SIGABRT_COMPAT 6
   52: #endif
   53: #ifdef SIGABRT_COMPAT
   54: # define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT)
   55: #else
   56: # define SIGABRT_COMPAT_MASK 0
   57: #endif
   58: 
   59: typedef void (*handler_t) (int);
   60: 
   61: /* Handling of gnulib defined signals.  */
   62: 
   63: #if GNULIB_defined_SIGPIPE
   64: static handler_t SIGPIPE_handler = SIG_DFL;
   65: #endif
   66: 
   67: #if GNULIB_defined_SIGPIPE
   68: static handler_t
   69: ext_signal (int sig, handler_t handler)
   70: {
   71:   switch (sig)
   72:     {
   73:     case SIGPIPE:
   74:       {
   75: 	handler_t old_handler = SIGPIPE_handler;
   76: 	SIGPIPE_handler = handler;
   77: 	return old_handler;
   78:       }
   79:     default: /* System defined signal */
   80:       return signal (sig, handler);
   81:     }
   82: }
   83: # define signal ext_signal
   84: #endif
   85: 
   86: int
   87: sigismember (const sigset_t *set, int sig)
   88: {
   89:   if (sig >= 0 && sig < NSIG)
   90:     {
   91:       #ifdef SIGABRT_COMPAT
   92:       if (sig == SIGABRT_COMPAT)
   93: 	sig = SIGABRT;
   94:       #endif
   95: 
   96:       return (*set >> sig) & 1;
   97:     }
   98:   else
   99:     return 0;
  100: }
  101: 
  102: int
  103: sigemptyset (sigset_t *set)
  104: {
  105:   *set = 0;
  106:   return 0;
  107: }
  108: 
  109: int
  110: sigaddset (sigset_t *set, int sig)
  111: {
  112:   if (sig >= 0 && sig < NSIG)
  113:     {
  114:       #ifdef SIGABRT_COMPAT
  115:       if (sig == SIGABRT_COMPAT)
  116: 	sig = SIGABRT;
  117:       #endif
  118: 
  119:       *set |= 1U << sig;
  120:       return 0;
  121:     }
  122:   else
  123:     {
  124:       errno = EINVAL;
  125:       return -1;
  126:     }
  127: }
  128: 
  129: int
  130: sigdelset (sigset_t *set, int sig)
  131: {
  132:   if (sig >= 0 && sig < NSIG)
  133:     {
  134:       #ifdef SIGABRT_COMPAT
  135:       if (sig == SIGABRT_COMPAT)
  136: 	sig = SIGABRT;
  137:       #endif
  138: 
  139:       *set &= ~(1U << sig);
  140:       return 0;
  141:     }
  142:   else
  143:     {
  144:       errno = EINVAL;
  145:       return -1;
  146:     }
  147: }
  148: 
  149: 
  150: int
  151: sigfillset (sigset_t *set)
  152: {
  153:   *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
  154:   return 0;
  155: }
  156: 
  157: /* Set of currently blocked signals.  */
  158: static volatile sigset_t blocked_set /* = 0 */;
  159: 
  160: /* Set of currently blocked and pending signals.  */
  161: static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
  162: 
  163: /* Signal handler that is installed for blocked signals.  */
  164: static void
  165: blocked_handler (int sig)
  166: {
  167:   /* Reinstall the handler, in case the signal occurs multiple times
  168:      while blocked.  There is an inherent race where an asynchronous
  169:      signal in between when the kernel uninstalled the handler and
  170:      when we reinstall it will trigger the default handler; oh
  171:      well.  */
  172:   signal (sig, blocked_handler);
  173:   if (sig >= 0 && sig < NSIG)
  174:     pending_array[sig] = 1;
  175: }
  176: 
  177: int
  178: sigpending (sigset_t *set)
  179: {
  180:   sigset_t pending = 0;
  181:   int sig;
  182: 
  183:   for (sig = 0; sig < NSIG; sig++)
  184:     if (pending_array[sig])
  185:       pending |= 1U << sig;
  186:   *set = pending;
  187:   return 0;
  188: }
  189: 
  190: /* The previous signal handlers.
  191:    Only the array elements corresponding to blocked signals are relevant.  */
  192: static volatile handler_t old_handlers[NSIG];
  193: 
  194: int
  195: sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
  196: {
  197:   if (old_set != NULL)
  198:     *old_set = blocked_set;
  199: 
  200:   if (set != NULL)
  201:     {
  202:       sigset_t new_blocked_set;
  203:       sigset_t to_unblock;
  204:       sigset_t to_block;
  205: 
  206:       switch (operation)
  207: 	{
  208: 	case SIG_BLOCK:
  209: 	  new_blocked_set = blocked_set | *set;
  210: 	  break;
  211: 	case SIG_SETMASK:
  212: 	  new_blocked_set = *set;
  213: 	  break;
  214: 	case SIG_UNBLOCK:
  215: 	  new_blocked_set = blocked_set & ~*set;
  216: 	  break;
  217: 	default:
  218: 	  errno = EINVAL;
  219: 	  return -1;
  220: 	}
  221:       to_unblock = blocked_set & ~new_blocked_set;
  222:       to_block = new_blocked_set & ~blocked_set;
  223: 
  224:       if (to_block != 0)
  225: 	{
  226: 	  int sig;
  227: 
  228: 	  for (sig = 0; sig < NSIG; sig++)
  229: 	    if ((to_block >> sig) & 1)
  230: 	      {
  231: 		pending_array[sig] = 0;
  232: 		if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
  233: 		  blocked_set |= 1U << sig;
  234: 	      }
  235: 	}
  236: 
  237:       if (to_unblock != 0)
  238: 	{
  239: 	  sig_atomic_t received[NSIG];
  240: 	  int sig;
  241: 
  242: 	  for (sig = 0; sig < NSIG; sig++)
  243: 	    if ((to_unblock >> sig) & 1)
  244: 	      {
  245: 		if (signal (sig, old_handlers[sig]) != blocked_handler)
  246: 		  /* The application changed a signal handler while the signal
  247: 		     was blocked, bypassing our rpl_signal replacement.
  248: 		     We don't support this.  */
  249: 		  abort ();
  250: 		received[sig] = pending_array[sig];
  251: 		blocked_set &= ~(1U << sig);
  252: 		pending_array[sig] = 0;
  253: 	      }
  254: 	    else
  255: 	      received[sig] = 0;
  256: 
  257: 	  for (sig = 0; sig < NSIG; sig++)
  258: 	    if (received[sig])
  259: 	      raise (sig);
  260: 	}
  261:     }
  262:   return 0;
  263: }
  264: 
  265: /* Install the handler FUNC for signal SIG, and return the previous
  266:    handler.  */
  267: handler_t
  268: rpl_signal (int sig, handler_t handler)
  269: {
  270:   /* We must provide a wrapper, so that a user can query what handler
  271:      they installed even if that signal is currently blocked.  */
  272:   if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
  273:       && handler != SIG_ERR)
  274:     {
  275:       #ifdef SIGABRT_COMPAT
  276:       if (sig == SIGABRT_COMPAT)
  277: 	sig = SIGABRT;
  278:       #endif
  279: 
  280:       if (blocked_set & (1U << sig))
  281: 	{
  282: 	  /* POSIX states that sigprocmask and signal are both
  283: 	     async-signal-safe.  This is not true of our
  284: 	     implementation - there is a slight data race where an
  285: 	     asynchronous interrupt on signal A can occur after we
  286: 	     install blocked_handler but before we have updated
  287: 	     old_handlers for signal B, such that handler A can see
  288: 	     stale information if it calls signal(B).  Oh well -
  289: 	     signal handlers really shouldn't try to manipulate the
  290: 	     installed handlers of unrelated signals.  */
  291: 	  handler_t result = old_handlers[sig];
  292: 	  old_handlers[sig] = handler;
  293: 	  return result;
  294: 	}
  295:       else
  296: 	return signal (sig, handler);
  297:     }
  298:   else
  299:     {
  300:       errno = EINVAL;
  301:       return SIG_ERR;
  302:     }
  303: }
  304: 
  305: #if GNULIB_defined_SIGPIPE
  306: /* Raise the signal SIG.  */
  307: int
  308: rpl_raise (int sig)
  309: # undef raise
  310: {
  311:   switch (sig)
  312:     {
  313:     case SIGPIPE:
  314:       if (blocked_set & (1U << sig))
  315: 	pending_array[sig] = 1;
  316:       else
  317: 	{
  318: 	  handler_t handler = SIGPIPE_handler;
  319: 	  if (handler == SIG_DFL)
  320: 	    exit (128 + SIGPIPE);
  321: 	  else if (handler != SIG_IGN)
  322: 	    (*handler) (sig);
  323: 	}
  324:       return 0;
  325:     default: /* System defined signal */
  326:       return raise (sig);
  327:     }
  328: }
  329: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>