File: fvwmsignal.c

package info (click to toggle)
fvwm 1:2.6.8-1
  • links: PTS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 15,804 kB
  • sloc: ansic: 145,770; xml: 17,093; perl: 7,302; sh: 4,921; makefile: 1,094; yacc: 688; lex: 187; sed: 11
file content (234 lines) | stat: -rw-r--r-- 6,129 bytes parent folder | download
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
228
229
230
231
232
233
234
/* -*-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 */

#ifdef C_ALLOCA
	/* If we're using the C version of alloca, see if anything needs to be
	 * freed up.
	 */
	alloca(0);
#endif

	/*
	 * 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 */