Oops, fix last commit.
[gnulib.git] / lib / sigprocmask.c
1 /* POSIX compatible signal blocking.
2    Copyright (C) 2006-2007 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   return pending;
111 }
112
113 /* The previous signal handlers.
114    Only the array elements corresponding to blocked signals are relevant.  */
115 static handler_t old_handlers[NSIG];
116
117 int
118 sigprocmask (int operation, const sigset_t *set, sigset_t *old_set)
119 {
120   if (old_set != NULL)
121     *old_set = blocked_set;
122
123   if (set != NULL)
124     {
125       sigset_t new_blocked_set;
126       sigset_t to_unblock;
127       sigset_t to_block;
128
129       switch (operation)
130         {
131         case SIG_BLOCK:
132           new_blocked_set = blocked_set | *set;
133           break;
134         case SIG_SETMASK:
135           new_blocked_set = *set;
136           break;
137         case SIG_UNBLOCK:
138           new_blocked_set = blocked_set & ~*set;
139           break;
140         default:
141           errno = EINVAL;
142           return -1;
143         }
144       to_unblock = blocked_set & ~new_blocked_set;
145       to_block = new_blocked_set & ~blocked_set;
146
147       if (to_block != 0)
148         {
149           int sig;
150
151           for (sig = 0; sig < NSIG; sig++)
152             if ((to_block >> sig) & 1)
153               {
154                 pending_array[sig] = 0;
155                 if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR)
156                   blocked_set |= 1U << sig;
157               }
158         }
159
160       if (to_unblock != 0)
161         {
162           sig_atomic_t received[NSIG];
163           int sig;
164
165           for (sig = 0; sig < NSIG; sig++)
166             if ((to_unblock >> sig) & 1)
167               {
168                 if (signal (sig, old_handlers[sig]) != blocked_handler)
169                   /* The application changed a signal handler while the signal
170                      was blocked.  We don't support this.  */
171                   abort ();
172                 received[sig] = pending_array[sig];
173                 blocked_set &= ~(1U << sig);
174                 pending_array[sig] = 0;
175               }
176             else
177               received[sig] = 0;
178
179           for (sig = 0; sig < NSIG; sig++)
180             if (received[sig])
181               {
182                 #if HAVE_RAISE
183                 raise (sig);
184                 #else
185                 kill (getpid (), sig);
186                 #endif
187               }
188         }
189     }
190   return 0;
191 }