1/* POSIX compatible signal blocking.
2   Copyright (C) 2006-2011 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
59typedef void (*handler_t) (int);
60
61/* Handling of gnulib defined signals.  */
62
63#if GNULIB_defined_SIGPIPE
64static handler_t SIGPIPE_handler = SIG_DFL;
65#endif
66
67#if GNULIB_defined_SIGPIPE
68static handler_t
69ext_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
86int
87sigismember (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
102int
103sigemptyset (sigset_t *set)
104{
105  *set = 0;
106  return 0;
107}
108
109int
110sigaddset (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
129int
130sigdelset (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
150int
151sigfillset (sigset_t *set)
152{
153  *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK;
154  return 0;
155}
156
157/* Set of currently blocked signals.  */
158static volatile sigset_t blocked_set /* = 0 */;
159
160/* Set of currently blocked and pending signals.  */
161static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
162
163/* Signal handler that is installed for blocked signals.  */
164static void
165blocked_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
177int
178sigpending (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.  */
192static volatile handler_t old_handlers[NSIG];
193
194int
195sigprocmask (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.  */
267handler_t
268rpl_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.  */
307int
308rpl_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
330