* lib/sigprocmask.c (sigprocmask): Fix typo.
[gnulib.git] / lib / sigprocmask.c
1 /* POSIX compatible signal blocking.
2    Copyright (C) 2006 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 2, or (at your option)
8    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, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "sigprocmask.h"
23
24 #include <errno.h>
25 #include <stdint.h>
26 #include <stdlib.h>
27
28 /* We assume that a platform without POSIX signal blocking functions also
29    does not have the POSIX sigaction() function, only the signal() function.
30    This is true for Woe32 platforms.  */
31
32 /* A signal handler.  */
33 typedef void (*handler_t) (int signal);
34
35 int
36 sigismember (const sigset_t *set, int sig)
37 {
38   if (sig >= 0 && sig < NSIG)
39     return (*set >> sig) & 1;
40   else
41     return 0;
42 }
43
44 int
45 sigemptyset (sigset_t *set)
46 {
47   *set = 0;
48   return 0;
49 }
50
51 int
52 sigaddset (sigset_t *set, int sig)
53 {
54   if (sig >= 0 && sig < NSIG)
55     {
56       *set |= 1U << sig;
57       return 0;
58     }
59   else
60     {
61       errno = EINVAL;
62       return -1;
63     }
64 }
65
66 int
67 sigdelset (sigset_t *set, int sig)
68 {
69   if (sig >= 0 && sig < NSIG)
70     {
71       *set &= ~(1U << sig);
72       return 0;
73     }
74   else
75     {
76       errno = EINVAL;
77       return -1;
78     }
79 }
80
81 int
82 sigfillset (sigset_t *set)
83 {
84   *set = (2U << (NSIG - 1)) - 1;
85   return 0;
86 }
87
88 /* Set of currently blocked signals.  */
89 static sigset_t blocked_set /* = 0 */;
90
91 /* Set of currently blocked and pending signals.  */
92 static volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */;
93
94 /* Signal handler that is installed for blocked signals.  */
95 static void
96 blocked_handler (int sig)
97 {
98   if (sig >= 0 && sig < NSIG)
99     pending_array[sig] = 1;
100 }
101
102 int
103 sigpending (sigset_t *set)
104 {
105   sigset_t pending = 0;
106   int sig;
107
108   for (sig = 0; sig < NSIG; sig++)
109     if (pending_array[sig])
110       pending |= 1U << sig;
111   return pending;
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               {
183                 #if HAVE_RAISE
184                 raise (sig);
185                 #else
186                 kill (getpid (), sig);
187                 #endif
188               }
189         }
190     }
191   return 0;
192 }