| /* POSIX compatible signal blocking. | |
| Copyright (C) 2008-2026 Free Software Foundation, Inc. | |
| Written by Eric Blake <ebb9@byu.net>, 2008. | |
| This file is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU Lesser General Public License as | |
| published by the Free Software Foundation; either version 2.1 of the | |
| License, or (at your option) any later version. | |
| This file is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU Lesser General Public License for more details. | |
| You should have received a copy of the GNU Lesser General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| #include <config.h> | |
| /* Specification. */ | |
| #include <signal.h> | |
| #include <errno.h> | |
| #include <stdint.h> | |
| #include <stdlib.h> | |
| /* This implementation of sigaction is tailored to native Windows behavior: | |
| signal() has SysV semantics (ie. the handler is uninstalled before | |
| it is invoked). This is an inherent data race if an asynchronous | |
| signal is sent twice in a row before we can reinstall our handler, | |
| but there's nothing we can do about it. Meanwhile, sigprocmask() | |
| is not present, and while we can use the gnulib replacement to | |
| provide critical sections, it too suffers from potential data races | |
| in the face of an ill-timed asynchronous signal. And we compound | |
| the situation by reading static storage in a signal handler, which | |
| POSIX warns is not generically async-signal-safe. Oh well. | |
| Additionally: | |
| - We don't implement SA_NOCLDSTOP or SA_NOCLDWAIT, because SIGCHLD | |
| is not defined. | |
| - We don't implement SA_ONSTACK, because sigaltstack() is not present. | |
| - We ignore SA_RESTART, because blocking native Windows API calls are | |
| not interrupted anyway when an asynchronous signal occurs, and the | |
| MSVCRT runtime never sets errno to EINTR. | |
| - We don't implement SA_SIGINFO because it is impossible to do so | |
| portably. | |
| POSIX states that an application should not mix signal() and | |
| sigaction(). We support the use of signal() within the gnulib | |
| sigprocmask() substitute, but all other application code linked | |
| with this module should stick with only sigaction(). */ | |
| /* Check some of our assumptions. */ | |
| #if defined SIGCHLD || defined HAVE_SIGALTSTACK || defined HAVE_SIGINTERRUPT | |
| # error "Revisit the assumptions made in the sigaction module" | |
| #endif | |
| /* Out-of-range substitutes make a good fallback for uncatchable | |
| signals. */ | |
| #ifndef SIGKILL | |
| # define SIGKILL (-1) | |
| #endif | |
| #ifndef SIGSTOP | |
| # define SIGSTOP (-1) | |
| #endif | |
| /* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias | |
| for the signal SIGABRT. Only one signal handler is stored for both | |
| SIGABRT and SIGABRT_COMPAT. SIGABRT_COMPAT is not a signal of its own. */ | |
| #if defined _WIN32 && ! defined __CYGWIN__ | |
| # undef SIGABRT_COMPAT | |
| # define SIGABRT_COMPAT 6 | |
| #endif | |
| /* A signal handler. */ | |
| typedef void (*handler_t) (int signal); | |
| /* Set of current actions. If sa_handler for an entry is NULL, then | |
| that signal is not currently handled by the sigaction handler. */ | |
| static struct sigaction volatile action_array[NSIG] /* = 0 */; | |
| /* Signal handler that is installed for signals. */ | |
| static void | |
| sigaction_handler (int sig) | |
| { | |
| int saved_errno = errno; | |
| if (sig < 0 || NSIG <= sig || !action_array[sig].sa_handler) | |
| { | |
| /* Unexpected situation; be careful to avoid recursive abort. */ | |
| if (sig == SIGABRT) | |
| signal (SIGABRT, SIG_DFL); | |
| abort (); | |
| } | |
| /* Reinstall the signal handler when required; otherwise update the | |
| bookkeeping so that the user's handler may call sigaction and get | |
| accurate results. We know the signal isn't currently blocked, or | |
| we wouldn't be in its handler, therefore we know that we are not | |
| interrupting a sigaction() call. There is a race where any | |
| asynchronous instance of the same signal occurring before we | |
| reinstall the handler will trigger the default handler; oh | |
| well. */ | |
| handler_t handler = action_array[sig].sa_handler; | |
| if ((action_array[sig].sa_flags & SA_RESETHAND) == 0) | |
| signal (sig, sigaction_handler); | |
| else | |
| action_array[sig].sa_handler = NULL; | |
| /* Block appropriate signals. */ | |
| sigset_t mask = action_array[sig].sa_mask; | |
| if ((action_array[sig].sa_flags & SA_NODEFER) == 0) | |
| sigaddset (&mask, sig); | |
| sigset_t oldmask; | |
| sigprocmask (SIG_BLOCK, &mask, &oldmask); | |
| /* Invoke the user's handler, then restore prior mask. */ | |
| errno = saved_errno; | |
| handler (sig); | |
| saved_errno = errno; | |
| sigprocmask (SIG_SETMASK, &oldmask, NULL); | |
| errno = saved_errno; | |
| } | |
| /* Change and/or query the action that will be taken on delivery of | |
| signal SIG. If not NULL, ACT describes the new behavior. If not | |
| NULL, OACT is set to the prior behavior. Return 0 on success, or | |
| set errno and return -1 on failure. */ | |
| int | |
| sigaction (int sig, const struct sigaction *restrict act, | |
| struct sigaction *restrict oact) | |
| { | |
| if (sig < 0 || NSIG <= sig || sig == SIGKILL || sig == SIGSTOP | |
| || (act && act->sa_handler == SIG_ERR)) | |
| { | |
| errno = EINVAL; | |
| return -1; | |
| } | |
| #ifdef SIGABRT_COMPAT | |
| if (sig == SIGABRT_COMPAT) | |
| sig = SIGABRT; | |
| #endif | |
| /* POSIX requires sigaction() to be async-signal-safe. In other | |
| words, if an asynchronous signal can occur while we are anywhere | |
| inside this function, the user's handler could then call | |
| sigaction() recursively and expect consistent results. We meet | |
| this rule by using sigprocmask to block all signals before | |
| modifying any data structure that could be read from a signal | |
| handler; this works since we know that the gnulib sigprocmask | |
| replacement does not try to use sigaction() from its handler. */ | |
| if (!act && !oact) | |
| return 0; | |
| sigset_t mask; | |
| sigfillset (&mask); | |
| sigset_t oldmask; | |
| sigprocmask (SIG_BLOCK, &mask, &oldmask); | |
| if (oact) | |
| { | |
| if (action_array[sig].sa_handler) | |
| *oact = action_array[sig]; | |
| else | |
| { | |
| /* Safe to change the handler at will here, since all | |
| signals are currently blocked. */ | |
| oact->sa_handler = signal (sig, SIG_DFL); | |
| if (oact->sa_handler == SIG_ERR) | |
| goto failure; | |
| signal (sig, oact->sa_handler); | |
| oact->sa_flags = SA_RESETHAND | SA_NODEFER; | |
| sigemptyset (&oact->sa_mask); | |
| } | |
| } | |
| if (act) | |
| { | |
| /* Safe to install the handler before updating action_array, | |
| since all signals are currently blocked. */ | |
| if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN) | |
| { | |
| if (signal (sig, act->sa_handler) == SIG_ERR) | |
| goto failure; | |
| action_array[sig].sa_handler = NULL; | |
| } | |
| else | |
| { | |
| if (signal (sig, sigaction_handler) == SIG_ERR) | |
| goto failure; | |
| action_array[sig] = *act; | |
| } | |
| } | |
| sigprocmask (SIG_SETMASK, &oldmask, NULL); | |
| return 0; | |
| failure: | |
| { | |
| int saved_errno = errno; | |
| sigprocmask (SIG_SETMASK, &oldmask, NULL); | |
| errno = saved_errno; | |
| return -1; | |
| } | |
| } |