xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/gnulib-lib/sigprocmask.c (revision c38e7cc395b1472a774ff828e46123de44c628e9)
1 /* POSIX compatible signal blocking.
2    Copyright (C) 2006 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 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