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