1 |
/* |
2 |
* Signal handling protocol definitions for ASCEND |
3 |
* May 27, 1997 |
4 |
* By Benjamin Andrew Allan |
5 |
* Version: $Revision: 1.9 $ |
6 |
* Version control file: $RCSfile: ascSignal.c,v $ |
7 |
* Date last modified: $Date: 1999/01/19 12:23:20 $ |
8 |
* Last modified by: $Author: mthomas $ |
9 |
* Part of Ascend |
10 |
* |
11 |
* This file is part of the Ascend Programming System. |
12 |
* |
13 |
* Copyright (C) 1997 Benjamin Andrew Allan |
14 |
* |
15 |
* The Ascend Programming System is free software; you can redistribute |
16 |
* it and/or modify it under the terms of the GNU General Public License as |
17 |
* published by the Free Software Foundation; either version 2 of the |
18 |
* License, or (at your option) any later version. |
19 |
* |
20 |
* ASCEND is distributed in hope that it will be |
21 |
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
23 |
* General Public License for more details. |
24 |
* |
25 |
* You should have received a copy of the GNU General Public License |
26 |
* along with the program; if not, write to the Free Software Foundation, |
27 |
* Inc., 675 Mass Ave, Cambridge, MA 02139 USA. Check the file named |
28 |
* COPYING. |
29 |
* |
30 |
*/ |
31 |
/* |
32 |
* Start of making signal handling in ASCEND |
33 |
* code somewhat sane. Still needs to somehow |
34 |
* support the management of jmp_buf's so that |
35 |
* handlers will longjmp to the right place. |
36 |
* |
37 |
* A better alternative is to make all our code check/return |
38 |
* status flags based on a variable set by the trap |
39 |
* and cleared by recipient. |
40 |
*/ |
41 |
/* |
42 |
* ChangeLog |
43 |
* |
44 |
* 10/15/2005 - Changed ascresetneeded() so that any previously |
45 |
* registered handlers are reset before return. |
46 |
* - Added Asc_SignalRecover() to standard handler |
47 |
* Asc_SignalTrap() so handlers are reset if necessary. (JDS) |
48 |
*/ |
49 |
|
50 |
#include <stdio.h> |
51 |
#include "utilities/ascConfig.h" |
52 |
#ifndef NO_SIGNAL_TRAPS |
53 |
#include <signal.h> |
54 |
#include <setjmp.h> |
55 |
#endif /* NO_SIGNAL_TRAPS*/ |
56 |
#ifdef __WIN32__ |
57 |
#include <process.h> |
58 |
#else |
59 |
#include <unistd.h> |
60 |
#endif |
61 |
#include "utilities/ascSignal.h" |
62 |
#include "general/list.h" |
63 |
|
64 |
|
65 |
static jmp_buf f_test_env; /* for local testing of signal handling */ |
66 |
|
67 |
#ifndef NO_SIGNAL_TRAPS |
68 |
/* test buf for initialization */ |
69 |
jmp_buf g_fpe_env; |
70 |
jmp_buf g_seg_env; |
71 |
jmp_buf g_int_env; |
72 |
|
73 |
/* for future use */ |
74 |
jmp_buf g_foreign_code_call_env; |
75 |
|
76 |
#endif /* NO_SIGNAL_TRAPS*/ |
77 |
|
78 |
static int f_reset_needed = -2; |
79 |
/* has value 0 or 1 after Init is called. |
80 |
* and if Init is called without the value -2 in f_reset_needed, |
81 |
* it will fail. |
82 |
*/ |
83 |
static struct gl_list_t *f_fpe_traps = NULL; |
84 |
static struct gl_list_t *f_int_traps = NULL; |
85 |
static struct gl_list_t *f_seg_traps = NULL; |
86 |
/* |
87 |
* Batch of globals because we don't want an array of |
88 |
* all possible signals, most of which are NULL entries. |
89 |
* Each list holds the stack of pointers to signal handlers. |
90 |
*/ |
91 |
|
92 |
/* function to throw an interrupt. system dependent. */ |
93 |
static int testdooley2(int sig) |
94 |
{ |
95 |
raise(sig); |
96 |
|
97 |
return 0; |
98 |
} |
99 |
|
100 |
/* function to catch an interrupt */ |
101 |
static void testctrlc(int signum) |
102 |
{ |
103 |
FPRINTF(ASCERR," signal %d caught ",signum); |
104 |
if (signum == SIGFPE) { |
105 |
FPRESET; |
106 |
} |
107 |
longjmp(f_test_env, signum); |
108 |
} |
109 |
|
110 |
/* |
111 |
* So far the following seem to need reset trapped signals after |
112 |
* a longjmp, or unconditionally. |
113 |
* HPUX cc -Aa -D_HPUX_SOURCE |
114 |
* Solaris cc |
115 |
* AIX xlc |
116 |
* IRIX cc |
117 |
* Windows |
118 |
* |
119 |
* The following retain the last trap set with or without a call to longjmp |
120 |
* and so don't need resetting of traps. |
121 |
* SunOS4 acc |
122 |
* OSF32 cc |
123 |
* NetBSD gcc 2.4.5 -ansi |
124 |
*/ |
125 |
/* This function tests the signal reseting of compilers using SIGINT. |
126 |
* It should not be called except when starting a process. |
127 |
* Return 0 for no reset needed, 1 for reset needed, and |
128 |
* -1 if the test fails (presuming program doesn't exit first.) |
129 |
* Side effects: |
130 |
* - a line is sent to ASCERR |
131 |
* - SIGINT is set to SIG_DFL if no handler was previously registered |
132 |
* - SIGFPE may be set to SIG_DFL if no handler was previously registered |
133 |
*/ |
134 |
static int ascresetneeded(void) { |
135 |
static int c=0; |
136 |
static int result; |
137 |
SigHandler lasttrap; |
138 |
volatile SigHandler savedtrap; |
139 |
|
140 |
result = 0; |
141 |
|
142 |
/* test interrupt */ |
143 |
savedtrap = signal(SIGINT, testctrlc); |
144 |
PRINTF("Testing signal %d %p\t%p\t", SIGINT, savedtrap, testctrlc); |
145 |
if (setjmp(f_test_env) == 0) { |
146 |
testdooley2(SIGINT); |
147 |
} else { |
148 |
c++; |
149 |
} |
150 |
if (c != 1) { |
151 |
PRINTF("Signal test failed. ASCEND unlikely to work on this hardware.\n"); |
152 |
result = -1; |
153 |
} |
154 |
lasttrap = signal(SIGINT, (NULL != savedtrap) ? savedtrap : SIG_DFL); |
155 |
PRINTF("%p\n",lasttrap); |
156 |
if (lasttrap != testctrlc) { |
157 |
result = 1; |
158 |
} |
159 |
|
160 |
if (result != 0) { |
161 |
return result; |
162 |
} |
163 |
|
164 |
c = 0; |
165 |
/* passed interrupt, check fpe */ |
166 |
savedtrap=signal(SIGFPE, testctrlc); |
167 |
PRINTF("Testing signal %d %p\t%p\t",SIGFPE, savedtrap, testctrlc); |
168 |
if (setjmp(f_test_env)==0) { |
169 |
testdooley2(SIGFPE); |
170 |
} else { |
171 |
c++; |
172 |
} |
173 |
if (c != 1) { |
174 |
PRINTF("Signal test failed. ASCEND unlikely to work on this hardware.\n"); |
175 |
result = -1; |
176 |
} |
177 |
lasttrap = signal(SIGFPE, (NULL != savedtrap) ? savedtrap : SIG_DFL); |
178 |
PRINTF("%p\n",lasttrap); |
179 |
if (lasttrap != testctrlc) { |
180 |
result = 1; |
181 |
} |
182 |
|
183 |
return result; |
184 |
} |
185 |
|
186 |
static |
187 |
void initstack (struct gl_list_t *traps, int sig) |
188 |
{ |
189 |
SigHandler old; |
190 |
old = signal(sig,SIG_DFL); |
191 |
if (old != SIG_ERR && old != SIG_DFL) { |
192 |
gl_append_ptr(traps,(VOIDPTR)old); |
193 |
(void)signal(sig,old); |
194 |
} |
195 |
} |
196 |
/* |
197 |
* Returns 0 if successful, 1 if out of memory, 2 otherwise. |
198 |
* Does not establish any traps, just the structures for |
199 |
* maintaining them. Pushes the existing traps, if any, on |
200 |
* the bottom of the created stacks. |
201 |
* Cannot be called twice successfully. |
202 |
*/ |
203 |
int Asc_SignalInit(void) |
204 |
{ |
205 |
if ((f_reset_needed != -2) || (FALSE == gl_pool_initialized())) { |
206 |
return 2; |
207 |
} |
208 |
f_fpe_traps = gl_create(MAX_TRAP_DEPTH); |
209 |
f_int_traps = gl_create(MAX_TRAP_DEPTH); |
210 |
f_seg_traps = gl_create(MAX_TRAP_DEPTH); |
211 |
if (f_fpe_traps == NULL || f_int_traps == NULL || f_seg_traps == NULL) { |
212 |
return 1; |
213 |
} |
214 |
#ifndef NO_SIGNAL_TRAPS |
215 |
/* push the old ones if any, on the stack. */ |
216 |
initstack(f_fpe_traps, SIGFPE); |
217 |
initstack(f_int_traps, SIGINT); |
218 |
initstack(f_seg_traps, SIGSEGV); |
219 |
|
220 |
f_reset_needed = ascresetneeded(); |
221 |
if (f_reset_needed < 0) { |
222 |
f_reset_needed = 1; |
223 |
return 2; |
224 |
} |
225 |
#endif /* NO_SIGNAL_TRAPS */ |
226 |
return 0; |
227 |
} |
228 |
|
229 |
/* |
230 |
* clears and destroys the stacks of signal handlers. |
231 |
*/ |
232 |
void Asc_SignalDestroy(void) |
233 |
{ |
234 |
gl_destroy(f_fpe_traps); |
235 |
gl_destroy(f_int_traps); |
236 |
gl_destroy(f_seg_traps); |
237 |
f_fpe_traps = f_int_traps = f_seg_traps = NULL; |
238 |
} |
239 |
|
240 |
static void reset_trap(int signum, struct gl_list_t *tlist) |
241 |
{ |
242 |
SigHandler tp; |
243 |
if (tlist != NULL && gl_length(tlist) > 0L) { |
244 |
tp = (SigHandler)gl_fetch(tlist, gl_length(tlist)); |
245 |
if (tp != SIG_ERR) { |
246 |
(void)signal(signum,tp); |
247 |
} |
248 |
} else { |
249 |
(void)signal(signum,SIG_DFL); |
250 |
} |
251 |
} |
252 |
/* This function reinstalls all the signal handlers this module |
253 |
* has been informed of. This should be called after every |
254 |
* trapped exception and at any other time when the status of |
255 |
* exception handlers may have become not well defined. |
256 |
* The most recently pushed handler is installed for each supported |
257 |
* signal. If nothing on stack, SIG_DFL gets installed. |
258 |
*_Note that if somebody installs a handler without going through |
259 |
* our push/pop, theirs is liable to be forgotten. |
260 |
*/ |
261 |
void Asc_SignalRecover(int force) { |
262 |
if (force || f_reset_needed > 0) { |
263 |
#ifndef NO_SIGNAL_TRAPS |
264 |
reset_trap(SIGFPE, f_fpe_traps); |
265 |
reset_trap(SIGINT, f_int_traps); |
266 |
reset_trap(SIGSEGV, f_seg_traps); |
267 |
#endif /* NO_SIGNAL_TRAPS */ |
268 |
} |
269 |
} |
270 |
|
271 |
/* |
272 |
* append a pointer to the list given, if the list is not full. |
273 |
*/ |
274 |
static int push_trap(struct gl_list_t *tlist, SigHandler tp) |
275 |
{ |
276 |
if (tlist == NULL) { |
277 |
return -1; |
278 |
} |
279 |
if (gl_length(tlist) == gl_capacity(tlist)) { |
280 |
return 1; |
281 |
} |
282 |
gl_append_ptr(tlist,(VOIDPTR)tp); |
283 |
return 0; |
284 |
} |
285 |
|
286 |
/* |
287 |
* Adds a handler to the stack of signal handlers for the given signal. |
288 |
* There is a maximum stack limit, so returns 1 if limit exceeded. |
289 |
* Returns -1 if stack of signal requested does not exist. |
290 |
* Pushing a NULL handler does NOT change anything at all. |
291 |
* On a successful return, the handler has been installed and will |
292 |
* remain installed until a Asc_SignalHandlerPop or another push. |
293 |
*/ |
294 |
int Asc_SignalHandlerPush(int signum, SigHandler tp) |
295 |
{ |
296 |
int err; |
297 |
if (tp == NULL) { |
298 |
return 0; |
299 |
} |
300 |
switch (signum) { |
301 |
case SIGFPE: |
302 |
err = push_trap(f_fpe_traps,tp); |
303 |
break; |
304 |
case SIGINT: |
305 |
err = push_trap(f_int_traps,tp); |
306 |
break; |
307 |
case SIGSEGV: |
308 |
err = push_trap(f_seg_traps,tp); |
309 |
break; |
310 |
default: |
311 |
return -1; |
312 |
} |
313 |
if (err != 0) { |
314 |
FPRINTF(ASCERR,"Asc_Signal (%d) stack limit exceeded.\n",signum); |
315 |
return err; |
316 |
} |
317 |
(void)signal(signum, tp); /* install */ |
318 |
return 0; |
319 |
} |
320 |
|
321 |
/* |
322 |
* Returns: 0 -ok, 2 NULL list input, 1 empty list input, |
323 |
* -1 mismatched input tp and stack data. |
324 |
*/ |
325 |
static int pop_trap(struct gl_list_t *tlist, SigHandler tp) |
326 |
{ |
327 |
SigHandler oldtrap; |
328 |
|
329 |
if (tlist == NULL) { |
330 |
return 2; |
331 |
} |
332 |
if (gl_length(tlist) == 0) { |
333 |
return 1; |
334 |
} |
335 |
oldtrap = (SigHandler)gl_fetch(tlist,gl_length(tlist)); |
336 |
gl_delete(tlist,gl_length(tlist),0); |
337 |
return (-(oldtrap != tp)); |
338 |
} |
339 |
|
340 |
int Asc_SignalHandlerPop(int signum, SigHandler tp) |
341 |
{ |
342 |
int err; |
343 |
switch (signum) { |
344 |
case SIGFPE: |
345 |
err = pop_trap(f_fpe_traps,tp); |
346 |
break; |
347 |
case SIGINT: |
348 |
err = pop_trap(f_int_traps,tp); |
349 |
break; |
350 |
case SIGSEGV: |
351 |
err = pop_trap(f_seg_traps,tp); |
352 |
break; |
353 |
default: |
354 |
return -1; |
355 |
} |
356 |
if (err != 0 && tp != NULL) { |
357 |
FPRINTF(ASCERR,"Asc_Signal (%d) stack pop mismatch.\n",signum); |
358 |
return err; |
359 |
} |
360 |
Asc_SignalRecover(TRUE); |
361 |
return 0; |
362 |
} |
363 |
|
364 |
void Asc_SignalTrap(int sigval) { |
365 |
#ifndef NO_SIGNAL_TRAPS |
366 |
switch(sigval) { |
367 |
case SIGFPE: |
368 |
FPRINTF(ASCERR,"Asc_SignalTrap: SIGFPE caught\n"); |
369 |
FPRESET; |
370 |
longjmp(g_fpe_env,sigval); |
371 |
break; |
372 |
case SIGINT: |
373 |
FPRINTF(ASCERR,"Asc_SignalTrap: SIGINT (Ctrl-C) caught\n"); |
374 |
longjmp(g_int_env,sigval); |
375 |
break; |
376 |
case SIGSEGV: |
377 |
FPRINTF(ASCERR,"Asc_SignalTrap: SIGSEGV (bad address) caught\n"); |
378 |
longjmp(g_seg_env,sigval); |
379 |
break; |
380 |
default: |
381 |
FPRINTF(ASCERR,"Asc_SignalTrap: Installed on unexpected signal (# %d).\n", sigval); |
382 |
FPRINTF(ASCERR,"Asc_SignalTrap: Returning ... who knows where."); |
383 |
break; |
384 |
} |
385 |
return; |
386 |
#else |
387 |
UNUSED_PARAMETER(sigval); |
388 |
#endif /* NO_SIGNAL_TRAPS */ |
389 |
} |