Use raise module consistently.
[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 also
28    does not have the POSIX sigaction() function, only the signal() function.
29    This is true for Woe32 platforms.  */
30
31 /* A signal handler.  */
32 typedef void (*handler_t) (int signal);
33
34 int
35 sigismember (const sigset_t *set, int sig)
36 {
37   if (sig >= 0 && sig < NSIG)
38     return (*set >> sig) & 1;
39   else
40     return 0;
41 }
42
43 int
44 sigemptyset (sigset_t *set)
45 {
46   *set = 0;
47   return 0;
48 }
49
50 int
51 sigaddset (sigset_t *set, int sig)
52 {
53   if (sig >= 0 && sig < NSIG)
54     {
55       *set |= 1U << sig;
56       return 0;
57     }
58   else
59     {
60       errno = EINVAL;
61       return -1;
62     }
63 }
64
65 int
66 sigdelset (sigset_t *set, int sig)
67 {
68   if (sig >= 0 && sig < NSIG)
69     {
70       *set &= ~(1U << sig);
71       return 0;
72     }
73   else
74     {
75       errno = EINVAL;
76       return -1;
77     }
78 }
79
80 int
81 sigfillset (sigset_t *set)
82 {
83   *set = (2U << (NSIG - 1)) - 1;
84   return 0;
85 }
86
87 /* Set of currently blocked signals.  */
88 static sigset_t blocked_set /* = 0 */;
89
90 /* Set of currently blocked and pending signals.  */
91 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
92
93 /* Signal handler that is installed for blocked signals.  */
94 static void
95 blocked_handler (int sig)
96 {
97   if (sig >= 0 && sig < NSIG)
98     pending_array[sig] = 1;
99 }
100
101 int
102 sigpending (sigset_t *set)
103 {
104   sigset_t pending = 0;
105   int sig;
106
107   for (sig = 0; sig < NSIG; sig++)
108     if (pending_array[sig])
109       pending |= 1U << sig;
110   *set = pending;
111   return 0;
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               raise (sig);
183         }
184     }
185   return 0;
186 }