1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
|
/* -*-c-*- */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see: <http://www.gnu.org/licenses/>
*/
/*
* fvwmsignal.c
* Written by Chris Rankin, rankinc@zipworld.com.au
*
*/
#include "config.h"
#include <sys/wait.h>
#include <setjmp.h>
#include <errno.h>
#include "fvwmsignal.h"
#define true 1
#define false 0
typedef enum { SIG_INIT=0, SIG_DONE } SIG_STATUS;
volatile sig_atomic_t isTerminated = false;
static volatile sig_atomic_t canJump = false;
#ifdef HAVE_SIGSETJMP
static sigjmp_buf deadJump;
#define SIGSETJMP(env, savesigs) sigsetjmp(env, savesigs)
#else
static jmp_buf deadJump;
#define SIGSETJMP(env, savesigs) setjmp(env)
#endif
#if defined(HAVE_SIGLONGJMP) && defined(HAVE_SIGSETJMP)
#define SIGLONGJMP(env, val) siglongjmp(env, val)
#else
#define SIGLONGJMP(env, val) longjmp(env, val)
#endif
/*
* Reap child processes, preventing them from becoming zombies.
* We do this asynchronously within the SIGCHLD handler so that
* "it just happens".
*/
RETSIGTYPE
fvwmReapChildren(int sig)
{
(void)sig;
BSD_BLOCK_SIGNALS;
/*
* This is a signal handler, AND SO MUST BE REENTRANT!
* Now the wait() functions are safe here, but please don't
* add anything unless you're SURE that the new functions
* (plus EVERYTHING they call) are also reentrant. There
* are very few functions which are truly safe.
*/
#if HAVE_WAITPID
while (waitpid(-1, NULL, WNOHANG) > 0)
{
/* nothing to do here */
}
#elif HAVE_WAIT3
while (wait3(NULL, WNOHANG, NULL) > 0)
{
/* nothing to do here */
}
#else
# error One of waitpid or wait3 is needed.
#endif
BSD_UNBLOCK_SIGNALS;
SIGNAL_RETURN;
}
#ifdef USE_BSD_SIGNALS
static int term_sigs;
/*
* fvwmSetSignalMask - store the set of mutually exclusive signals
* away for future reference. This prevents different signals from
* trying to access the same static data at the same time.
*
* NOTE: We don't need this if we have POSIX.1 since we can install
* a signal mask automatically using sigaction()
*/
void
fvwmSetSignalMask(int sigmask)
{
term_sigs = sigmask;
}
/*
* fvwmGetSignalMask - get the set of signals that will terminate fvwm
*
* NOTE: We don't need this if we have POSIX.1 since we can install
* a signal mask automatically using sigaction()
*/
int
fvwmGetSignalMask(void)
{
return term_sigs;
}
#endif
/*
* fvwmSetTerminate - set the "end-of-execution" flag.
* This function should ONLY be called at the end of a
* signal handler. It is an integral part of the mechanism
* to stop system calls from blocking once the process is
* finished.
*
* NOTE: This is NOT a signal handler function in its own right!
*/
void
fvwmSetTerminate(int sig)
{
BSD_BLOCK_SIGNALS;
isTerminated = true;
if (canJump)
{
canJump = false;
/*
* This non-local jump is safe ONLY because we haven't called
* any non-reentrant functions in the short period where the
* "canJump" variable is true.
*
* NOTE: No need to restore the signal mask, since siglongjmp
* is designed to do that for us.
*/
SIGLONGJMP(deadJump, SIG_DONE);
}
BSD_UNBLOCK_SIGNALS;
}
#ifdef HAVE_SELECT
/*
* fvwmSelect - wrapper around the select() system call.
* This system call may block indefinitely. We don't want
* to block at all if the "terminate" flag is set - we
* just want it to fail as quickly as possible.
*/
int
fvwmSelect(fd_set_size_t nfds,
fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout)
{
volatile int iRet = -1; /* This variable MUST NOT be in a register */
/*
* Yes, we trash errno here, but you're only supposed to check
* errno immediately after a function fails anyway. If we fail,
* then it's because we received a signal. If we succeed, we
* shouldn't be checking errno. And if somebody calls us expecting
* us to preserve errno then that's their bug.
*
* NOTE: We mustn't call any function that might trash errno
* ourselves, except select() itself of course. I believe
* that sigsetjmp() does NOT trash errno.
*/
errno = EINTR;
/*
* Now initialise the non-local jump. Between here and the end of
* the routine (more-or-less) we must NOT call any non-reentrant
* functions! This is because we might need to abandon them half
* way through execution and return here!
*/
if ( SIGSETJMP(deadJump, 1) == SIG_INIT )
{
/*
* Activate the non-local jump. Between now and when we turn the
* jump off again, we must NOT call any non-reentrant functions
* because we could be interrupted halfway through ...
*/
canJump = true;
/*
* If we have already been told to terminate then we will not
* execute the select() because the flag will be set. If a
* "terminate" signal arrives between testing the flag and
* calling select() then we will jump back to the non-local
* jump point ...
*/
if ( !isTerminated )
{
/*
* The "die" signal will interrupt this system call:
* that IS the whole point, after all :-)
*/
iRet = select(nfds,
SELECT_FD_SET_CAST readfds,
SELECT_FD_SET_CAST writefds,
SELECT_FD_SET_CAST exceptfds,
timeout);
}
/*
* The non-local jump is about to go out of scope,
* so we must deactivate it. Note that the return-
* value from select() will be safely stored in the
* local variable before the jump is disabled.
*/
canJump = false;
}
return iRet;
}
#endif /* HAVE_SELECT */
|