Use sigaction module rather than signal().
[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 #include "sig-handler.h"
28
29 /* We assume that a platform without POSIX signal blocking functions
30    also does not have the POSIX sigaction() function, only the
31    signal() function.  We also assume signal() has SysV semantics,
32    where any handler is uninstalled prior to being invoked.  This is
33    true for Woe32 platforms.  */
34
35 /* We use raw signal(), but also provide a wrapper rpl_signal() so
36    that applications can query or change a blocked signal.  */
37 #undef signal
38
39 /* Provide invalid signal numbers as fallbacks if the uncatchable
40    signals are not defined.  */
41 #ifndef SIGKILL
42 # define SIGKILL (-1)
43 #endif
44 #ifndef SIGSTOP
45 # define SIGSTOP (-1)
46 #endif
47
48 int
49 sigismember (const sigset_t *set, int sig)
50 {
51   if (sig >= 0 && sig < NSIG)
52     return (*set >> sig) & 1;
53   else
54     return 0;
55 }
56
57 int
58 sigemptyset (sigset_t *set)
59 {
60   *set = 0;
61   return 0;
62 }
63
64 int
65 sigaddset (sigset_t *set, int sig)
66 {
67   if (sig >= 0 && sig < NSIG)
68     {
69       *set |= 1U << sig;
70       return 0;
71     }
72   else
73     {
74       errno = EINVAL;
75       return -1;
76     }
77 }
78
79 int
80 sigdelset (sigset_t *set, int sig)
81 {
82   if (sig >= 0 && sig < NSIG)
83     {
84       *set &= ~(1U << sig);
85       return 0;
86     }
87   else
88     {
89       errno = EINVAL;
90       return -1;
91     }
92 }
93
94 int
95 sigfillset (sigset_t *set)
96 {
97   *set = (2U << (NSIG - 1)) - 1;
98   return 0;
99 }
100
101 /* Set of currently blocked signals.  */
102 static volatile sigset_t blocked_set /* = 0 */;
103
104 /* Set of currently blocked and pending signals.  */
105 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
106
107 /* Signal handler that is installed for blocked signals.  */
108 static void
109 blocked_handler (int sig)
110 {
111   /* Reinstall the handler, in case the signal occurs multiple times
112      while blocked.  There is an inherent race where an asynchronous
113      signal in between when the kernel uninstalled the handler and
114      when we reinstall it will trigger the default handler; oh
115      well.  */
116   signal (sig, blocked_handler);
117   if (sig >= 0 && sig < NSIG)
118     pending_array[sig] = 1;
119 }
120
121 int
122 sigpending (sigset_t *set)
123 {
124   sigset_t pending = 0;
125   int sig;
126
127   for (sig = 0; sig < NSIG; sig++)
128     if (pending_array[sig])
129       pending |= 1U << sig;
130   *set = pending;
131   return 0;
132 }
133
134 /* The previous signal handlers.
135    Only the array elements corresponding to blocked signals are relevant.  */
136 static volatile sa_handler_t old_handlers[NSIG];
137
138 int
139 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
140 {
141   if (old_set != NULL)
142     *old_set = blocked_set;
143
144   if (set != NULL)
145     {
146       sigset_t new_blocked_set;
147       sigset_t to_unblock;
148       sigset_t to_block;
149
150       switch (operation)
151         {
152         case SIG_BLOCK:
153           new_blocked_set = blocked_set | *set;
154           break;
155         case SIG_SETMASK:
156           new_blocked_set = *set;
157           break;
158         case SIG_UNBLOCK:
159           new_blocked_set = blocked_set & ~*set;
160           break;
161         default:
162           errno = EINVAL;
163           return -1;
164         }
165       to_unblock = blocked_set & ~new_blocked_set;
166       to_block = new_blocked_set & ~blocked_set;
167
168       if (to_block != 0)
169         {
170           int sig;
171
172           for (sig = 0; sig < NSIG; sig++)
173             if ((to_block >> sig) & 1)
174               {
175                 pending_array[sig] = 0;
176                 if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
177                   blocked_set |= 1U << sig;
178               }
179         }
180
181       if (to_unblock != 0)
182         {
183           sig_atomic_t received[NSIG];
184           int sig;
185
186           for (sig = 0; sig < NSIG; sig++)
187             if ((to_unblock >> sig) & 1)
188               {
189                 if (signal (sig, old_handlers[sig]) != blocked_handler)
190                   /* The application changed a signal handler while the signal
191                      was blocked.  We don't support this.  */
192                   abort ();
193                 received[sig] = pending_array[sig];
194                 blocked_set &= ~(1U << sig);
195                 pending_array[sig] = 0;
196               }
197             else
198               received[sig] = 0;
199
200           for (sig = 0; sig < NSIG; sig++)
201             if (received[sig])
202               raise (sig);
203         }
204     }
205   return 0;
206 }
207
208 /* Install the handler FUNC for signal SIG, and return the previous
209    handler.  */
210 sa_handler_t
211 rpl_signal (int sig, sa_handler_t handler)
212 {
213   /* We must provide a wrapper, so that a user can query what handler
214      they installed even if that signal is currently blocked.  */
215   if (sig >= 0 && sig < NSIG && sig != SIGKILL && sig != SIGSTOP
216       && handler != SIG_ERR)
217     {
218       if (blocked_set & (1U << sig))
219         {
220           /* POSIX states that sigprocmask and signal are both
221              async-signal-safe.  This is not true of our
222              implementation - there is a slight data race where an
223              asynchronous interrupt on signal A can occur after we
224              install blocked_handler but before we have updated
225              old_handlers for signal B, such that handler A can see
226              stale information if it calls signal(B).  Oh well -
227              signal handlers really shouldn't try to manipulate the
228              installed handlers of unrelated signals.  */
229           sa_handler_t result = old_handlers[sig];
230           old_handlers[sig] = handler;
231           return result;
232         }
233       return signal (sig, handler);
234     }
235   else
236     {
237       errno = EINVAL;
238       return SIG_ERR;
239     }
240 }