Support signal handling with SIGPIPE on native Windows platforms.
[gnulib.git] / lib / sigprocmask.c
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 typedef void (*handler_t) (int);
47
48 /* Handling of gnulib defined signals.  */
49
50 #if GNULIB_defined_SIGPIPE
51 static handler_t SIGPIPE_handler = SIG_DFL;
52 #endif
53
54 #if GNULIB_defined_SIGPIPE
55 static handler_t
56 ext_signal (int sig, handler_t handler)
57 {
58   switch (sig)
59     {
60     case SIGPIPE:
61       {
62         handler_t old_handler = SIGPIPE_handler;
63         SIGPIPE_handler = handler;
64         return old_handler;
65       }
66     default: /* System defined signal */
67       return signal (sig, handler);
68     }
69 }
70 # define signal ext_signal
71 #endif
72
73 int
74 sigismember (const sigset_t *set, int sig)
75 {
76   if (sig >= 0 && sig < NSIG)
77     return (*set >> sig) & 1;
78   else
79     return 0;
80 }
81
82 int
83 sigemptyset (sigset_t *set)
84 {
85   *set = 0;
86   return 0;
87 }
88
89 int
90 sigaddset (sigset_t *set, int sig)
91 {
92   if (sig >= 0 && sig < NSIG)
93     {
94       *set |= 1U << sig;
95       return 0;
96     }
97   else
98     {
99       errno = EINVAL;
100       return -1;
101     }
102 }
103
104 int
105 sigdelset (sigset_t *set, int sig)
106 {
107   if (sig >= 0 && sig < NSIG)
108     {
109       *set &= ~(1U << sig);
110       return 0;
111     }
112   else
113     {
114       errno = EINVAL;
115       return -1;
116     }
117 }
118
119 int
120 sigfillset (sigset_t *set)
121 {
122   *set = (2U << (NSIG - 1)) - 1;
123   return 0;
124 }
125
126 /* Set of currently blocked signals.  */
127 static volatile sigset_t blocked_set /* = 0 */;
128
129 /* Set of currently blocked and pending signals.  */
130 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
131
132 /* Signal handler that is installed for blocked signals.  */
133 static void
134 blocked_handler (int sig)
135 {
136   /* Reinstall the handler, in case the signal occurs multiple times
137      while blocked.  There is an inherent race where an asynchronous
138      signal in between when the kernel uninstalled the handler and
139      when we reinstall it will trigger the default handler; oh
140      well.  */
141   signal (sig, blocked_handler);
142   if (sig >= 0 && sig < NSIG)
143     pending_array[sig] = 1;
144 }
145
146 int
147 sigpending (sigset_t *set)
148 {
149   sigset_t pending = 0;
150   int sig;
151
152   for (sig = 0; sig < NSIG; sig++)
153     if (pending_array[sig])
154       pending |= 1U << sig;
155   *set = pending;
156   return 0;
157 }
158
159 /* The previous signal handlers.
160    Only the array elements corresponding to blocked signals are relevant.  */
161 static volatile handler_t old_handlers[NSIG];
162
163 int
164 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
165 {
166   if (old_set != NULL)
167     *old_set = blocked_set;
168
169   if (set != NULL)
170     {
171       sigset_t new_blocked_set;
172       sigset_t to_unblock;
173       sigset_t to_block;
174
175       switch (operation)
176         {
177         case SIG_BLOCK:
178           new_blocked_set = blocked_set | *set;
179           break;
180         case SIG_SETMASK:
181           new_blocked_set = *set;
182           break;
183         case SIG_UNBLOCK:
184           new_blocked_set = blocked_set & ~*set;
185           break;
186         default:
187           errno = EINVAL;
188           return -1;
189         }
190       to_unblock = blocked_set & ~new_blocked_set;
191       to_block = new_blocked_set & ~blocked_set;
192
193       if (to_block != 0)
194         {
195           int sig;
196
197           for (sig = 0; sig < NSIG; sig++)
198             if ((to_block >> sig) & 1)
199               {
200                 pending_array[sig] = 0;
201                 if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
202                   blocked_set |= 1U << sig;
203               }
204         }
205
206       if (to_unblock != 0)
207         {
208           sig_atomic_t received[NSIG];
209           int sig;
210
211           for (sig = 0; sig < NSIG; sig++)
212             if ((to_unblock >> sig) & 1)
213               {
214                 if (signal (sig, old_handlers[sig]) != blocked_handler)
215                   /* The application changed a signal handler while the signal
216                      was blocked, bypassing our rpl_signal replacement.
217                      We don't support this.  */
218                   abort ();
219                 received[sig] = pending_array[sig];
220                 blocked_set &= ~(1U << sig);
221                 pending_array[sig] = 0;
222               }
223             else
224               received[sig] = 0;
225
226           for (sig = 0; sig < NSIG; sig++)
227             if (received[sig])
228               raise (sig);
229         }
230     }
231   return 0;
232 }
233
234 /* Install the handler FUNC for signal SIG, and return the previous
235    handler.  */
236 handler_t
237 rpl_signal (int sig, handler_t handler)
238 {
239   /* We must provide a wrapper, so that a user can query what handler
240      they installed even if that signal is currently blocked.  */
241   if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
242       && handler != SIG_ERR)
243     {
244       if (blocked_set & (1U << sig))
245         {
246           /* POSIX states that sigprocmask and signal are both
247              async-signal-safe.  This is not true of our
248              implementation - there is a slight data race where an
249              asynchronous interrupt on signal A can occur after we
250              install blocked_handler but before we have updated
251              old_handlers for signal B, such that handler A can see
252              stale information if it calls signal(B).  Oh well -
253              signal handlers really shouldn't try to manipulate the
254              installed handlers of unrelated signals.  */
255           handler_t result = old_handlers[sig];
256           old_handlers[sig] = handler;
257           return result;
258         }
259       else
260         return signal (sig, handler);
261     }
262   else
263     {
264       errno = EINVAL;
265       return SIG_ERR;
266     }
267 }
268
269 #if GNULIB_defined_SIGPIPE
270 /* Raise the signal SIG.  */
271 int
272 rpl_raise (int sig)
273 # undef raise
274 {
275   switch (sig)
276     {
277     case SIGPIPE:
278       if (blocked_set & (1U << sig))
279         pending_array[sig] = 1;
280       else
281         {
282           handler_t handler = SIGPIPE_handler;
283           if (handler == SIG_DFL)
284             exit (128 + SIGPIPE);
285           else if (handler != SIG_IGN)
286             (*handler) (sig);
287         }
288       return 0;
289     default: /* System defined signal */
290       return raise (sig);
291     }
292 }
293 #endif