1 |
/* ASCEND modelling environment |
2 |
Copyright (C) 1997 Benjamin Andrew Allan |
3 |
Copyright (C) 2006 Carnegie Mellon University |
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 2, or (at your option) |
8 |
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, write to the Free Software |
17 |
Foundation, Inc., 59 Temple Place - Suite 330, |
18 |
Boston, MA 02111-1307, USA. |
19 |
*//** @file |
20 |
Signal handling protocol definitions for ASCEND |
21 |
|
22 |
Start of making signal handling in ASCEND |
23 |
code somewhat sane. Still needs to somehow |
24 |
support the management of jmp_buf's so that |
25 |
handlers will longjmp to the right place. |
26 |
|
27 |
A better alternative is to make all our code check/return |
28 |
status flags based on a variable set by the trap |
29 |
and cleared by recipient. |
30 |
*//* |
31 |
by Benjamin Andrew Allan, May 27, 1997 |
32 |
Last in CVS $Revision: 1.9 $ $Date: 1999/01/19 12:23:20 $ $Author: mthomas $ |
33 |
|
34 |
ChangeLog |
35 |
|
36 |
10/15/2005 - Changed ascresetneeded() so that any previously |
37 |
registered handlers are reset before return. |
38 |
- Added Asc_SignalRecover() to standard handler |
39 |
Asc_SignalTrap() so handlers are reset if necessary. (JDS) |
40 |
12/10/2005 - Changed storage of signal handlers from gl_list's |
41 |
to local arrays. gl_list's can't legally hold |
42 |
function pointers. (JDS) |
43 |
*/ |
44 |
|
45 |
#include <stdio.h> |
46 |
#include "ascConfig.h" |
47 |
|
48 |
#ifndef NO_SIGNAL_TRAPS |
49 |
# include <signal.h> |
50 |
# include <setjmp.h> |
51 |
#endif /* NO_SIGNAL_TRAPS*/ |
52 |
|
53 |
#ifdef __WIN32__ |
54 |
# include <process.h> |
55 |
#else |
56 |
# include <unistd.h> |
57 |
#endif |
58 |
|
59 |
#include "ascMalloc.h" |
60 |
#include "ascSignal.h" |
61 |
|
62 |
#if !defined(NO_SIGINT_TRAP) || !defined(NO_SIGSEGV_TRAP) |
63 |
static jmp_buf f_test_env; /* for local testing of signal handling */ |
64 |
#endif |
65 |
|
66 |
#ifndef NO_SIGNAL_TRAPS |
67 |
/* test buf for initialization */ |
68 |
jmp_buf g_fpe_env; |
69 |
jmp_buf g_seg_env; |
70 |
jmp_buf g_int_env; |
71 |
|
72 |
/* for future use */ |
73 |
jmp_buf g_foreign_code_call_env; |
74 |
|
75 |
#endif /* NO_SIGNAL_TRAPS*/ |
76 |
|
77 |
static int f_reset_needed = -2; |
78 |
/* has value 0 or 1 after Init is called. |
79 |
* and if Init is called without the value -2 in f_reset_needed, |
80 |
* it will fail. |
81 |
*/ |
82 |
static SigHandlerFn **f_fpe_traps = NULL; /**< array for pushed SIGFPE handlers */ |
83 |
static int f_fpe_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */ |
84 |
|
85 |
static SigHandlerFn **f_int_traps = NULL; /**< array for pushed SIGINT handlers */ |
86 |
static int f_int_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */ |
87 |
|
88 |
static SigHandlerFn **f_seg_traps = NULL; /**< array for pushed SIGSEGV handlers */ |
89 |
static int f_seg_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */ |
90 |
|
91 |
#ifndef NO_SIGSEGV_TRAP |
92 |
/* function to throw an interrupt. system dependent. */ |
93 |
static int testdooley2(int sig){ |
94 |
raise(sig); |
95 |
return 0; |
96 |
} |
97 |
#endif |
98 |
|
99 |
#if !defined(NO_SIGINT_TRAP) || !defined(NO_SIGSEGV_TRAP) |
100 |
/* function to catch an interrupt */ |
101 |
static void testcatch(int signum){ |
102 |
FPRINTF(ASCERR," signal %d caught ",signum); |
103 |
if (signum == SIGFPE) { |
104 |
FPRESET; |
105 |
} |
106 |
longjmp(f_test_env, signum); |
107 |
} |
108 |
#endif |
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 |
|
126 |
/** |
127 |
This function tests the signal reseting of compilers using SIGINT. |
128 |
It should not be called except when starting a process. |
129 |
|
130 |
Side effects: |
131 |
- a line is sent to ASCERR |
132 |
- SIGINT is set to SIG_DFL if no handler was previously registered |
133 |
- SIGFPE may be set to SIG_DFL if no handler was previously registered |
134 |
|
135 |
@return 0 for no reset needed, 1 for reset needed, and |
136 |
-1 if the test fails (presuming program doesn't exit first.) |
137 |
*/ |
138 |
static int ascresetneeded(void) { |
139 |
static int result = 0; |
140 |
|
141 |
#if !defined(NO_SIGINT_TRAP) || !defined(NO_SIGSEGV_TRAP) |
142 |
SigHandlerFn *lasttrap; |
143 |
volatile SigHandlerFn *savedtrap; |
144 |
static int c=0; |
145 |
#endif |
146 |
|
147 |
#ifndef NO_SIGINT_TRAP |
148 |
|
149 |
/* test interrupt */ |
150 |
savedtrap = signal(SIGINT, testcatch); |
151 |
CONSOLE_DEBUG("Testing signal SIGINT (signum = %d) %p\t%p\t", SIGINT, savedtrap, testcatch); |
152 |
if (setjmp(f_test_env) == 0) { |
153 |
testdooley2(SIGINT); |
154 |
} else { |
155 |
c++; |
156 |
} |
157 |
if (c != 1) { |
158 |
CONSOLE_DEBUG("SIGINT test failed"); |
159 |
ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"Signal (SIGINT) test failed. ASCEND unlikely to work on this hardware."); |
160 |
result = -1; |
161 |
} |
162 |
lasttrap = signal(SIGINT, (NULL != savedtrap) ? savedtrap : SIG_DFL); |
163 |
CONSOLE_DEBUG("%p",lasttrap); |
164 |
if (lasttrap != testcatch) { |
165 |
result = 1; |
166 |
} |
167 |
|
168 |
if (result != 0) { |
169 |
return result; |
170 |
} |
171 |
|
172 |
c = 0; |
173 |
#else |
174 |
CONSOLE_DEBUG("SIGINT trap bypassed: compile-time settings"); |
175 |
#endif |
176 |
|
177 |
#ifndef NO_SIGSEGV_TRAP |
178 |
/* passed interrupt, check fpe */ |
179 |
savedtrap=signal(SIGFPE, testcatch); |
180 |
CONSOLE_DEBUG("Testing signal %d %p\t%p\t",SIGFPE, savedtrap, testcatch); |
181 |
if (setjmp(f_test_env)==0) { |
182 |
testdooley2(SIGFPE); |
183 |
} else { |
184 |
c++; |
185 |
} |
186 |
if (c != 1) { |
187 |
CONSOLE_DEBUG("SIGFPE test failed"); |
188 |
ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"Signal test failed. ASCEND unlikely to work on this hardware."); |
189 |
result = -1; |
190 |
} |
191 |
lasttrap = signal(SIGFPE, (NULL != savedtrap) ? savedtrap : SIG_DFL); |
192 |
CONSOLE_DEBUG("%p\n",lasttrap); |
193 |
if (lasttrap != testcatch) { |
194 |
result = 1; |
195 |
} |
196 |
#else |
197 |
CONSOLE_DEBUG("SIGSEGV trap bypassed: compile-time settings."); |
198 |
#endif |
199 |
|
200 |
return result; |
201 |
} |
202 |
|
203 |
static void initstack (SigHandlerFn **traps, int *stackptr, int sig){ |
204 |
SigHandlerFn *old; |
205 |
old = signal(sig,SIG_DFL); |
206 |
if (old != SIG_ERR && old != SIG_DFL) { |
207 |
traps[0] = old; |
208 |
*stackptr = 0; |
209 |
(void)signal(sig,old); |
210 |
} |
211 |
} |
212 |
|
213 |
/** |
214 |
Initialise ASCEND signal handling |
215 |
|
216 |
Does not establish any traps, just the structures for maintaining them. |
217 |
Pushes the existing traps, if any, on the bottom of the created stacks. |
218 |
|
219 |
@NOTE Cannot be called twice successfully. |
220 |
|
221 |
@return 0 if successful, 1 if out of memory, 2 otherwise. |
222 |
*/ |
223 |
int Asc_SignalInit(void) |
224 |
{ |
225 |
if (f_reset_needed != -2) { |
226 |
return 2; |
227 |
} |
228 |
|
229 |
/* initialize SIGFPE stack */ |
230 |
if (f_fpe_traps == NULL) { |
231 |
f_fpe_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH); |
232 |
if (f_fpe_traps == NULL) { |
233 |
return 1; |
234 |
} |
235 |
} |
236 |
f_fpe_top_of_stack = -1; |
237 |
|
238 |
/* initialize SIGINT stack */ |
239 |
if (f_int_traps == NULL) { |
240 |
f_int_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH); |
241 |
if (f_int_traps == NULL) { |
242 |
ascfree(f_fpe_traps); |
243 |
f_fpe_traps = NULL; |
244 |
return 1; |
245 |
} |
246 |
} |
247 |
f_int_top_of_stack = -1; |
248 |
|
249 |
/* initialize SIGSEGV stack */ |
250 |
if (f_seg_traps == NULL) { |
251 |
f_seg_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH); |
252 |
if (f_seg_traps == NULL) { |
253 |
ascfree(f_fpe_traps); |
254 |
f_fpe_traps = NULL; |
255 |
ascfree(f_int_traps); |
256 |
f_int_traps = NULL; |
257 |
return 1; |
258 |
} |
259 |
} |
260 |
f_seg_top_of_stack = -1; |
261 |
|
262 |
#ifndef NO_SIGNAL_TRAPS |
263 |
/* push the old ones if any, on the stack. */ |
264 |
initstack(f_fpe_traps, &f_fpe_top_of_stack, SIGFPE); |
265 |
|
266 |
# ifndef NO_SIGINT_TRAP |
267 |
initstack(f_int_traps, &f_int_top_of_stack, SIGINT); |
268 |
# endif |
269 |
|
270 |
# ifndef NO_SIGSEGV_TRAP |
271 |
initstack(f_seg_traps, &f_seg_top_of_stack, SIGSEGV); |
272 |
# endif |
273 |
|
274 |
f_reset_needed = ascresetneeded(); |
275 |
if (f_reset_needed < 0) { |
276 |
f_reset_needed = 1; |
277 |
return 2; |
278 |
} |
279 |
#endif /* NO_SIGNAL_TRAPS */ |
280 |
return 0; |
281 |
} |
282 |
|
283 |
/** |
284 |
Clears and destroys the stacks of signal handlers. |
285 |
*/ |
286 |
void Asc_SignalDestroy(void) |
287 |
{ |
288 |
ascfree(f_fpe_traps); |
289 |
ascfree(f_int_traps); |
290 |
ascfree(f_seg_traps); |
291 |
f_fpe_traps = f_int_traps = f_seg_traps = NULL; |
292 |
f_fpe_top_of_stack = f_int_top_of_stack = f_seg_top_of_stack = -1; |
293 |
} |
294 |
|
295 |
static void reset_trap(int signum, SigHandlerFn **tlist, int tos) |
296 |
{ |
297 |
SigHandlerFn *tp; |
298 |
if ((tlist != NULL) && (tos >= 0) && (tos < MAX_TRAP_DEPTH)) { |
299 |
tp = tlist[tos]; |
300 |
if (tp != SIG_ERR) { |
301 |
(void)signal(signum,tp); |
302 |
} |
303 |
} else { |
304 |
(void)signal(signum,SIG_DFL); |
305 |
} |
306 |
} |
307 |
/* |
308 |
This function reinstalls all the signal handlers this module |
309 |
has been informed of. This should be called after every |
310 |
trapped exception and at any other time when the status of |
311 |
exception handlers may have become not well defined. |
312 |
The most recently pushed handler is installed for each supported |
313 |
signal. If nothing on stack, SIG_DFL gets installed. |
314 |
|
315 |
@NOTE that if somebody installs a handler without going through |
316 |
our push/pop, theirs is liable to be forgotten. |
317 |
*/ |
318 |
void Asc_SignalRecover(int force) |
319 |
{ |
320 |
if (force || f_reset_needed > 0) { |
321 |
#ifndef NO_SIGNAL_TRAPS |
322 |
reset_trap(SIGFPE, f_fpe_traps, f_fpe_top_of_stack); |
323 |
reset_trap(SIGINT, f_int_traps, f_int_top_of_stack); |
324 |
reset_trap(SIGSEGV, f_seg_traps, f_seg_top_of_stack); |
325 |
#endif /* NO_SIGNAL_TRAPS */ |
326 |
} |
327 |
} |
328 |
|
329 |
/** |
330 |
Append a pointer to the list given, if the list is not full. |
331 |
*/ |
332 |
static int push_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp) |
333 |
{ |
334 |
if (tlist == NULL) { |
335 |
CONSOLE_DEBUG("TLIST IS NULL"); |
336 |
return -1; |
337 |
} |
338 |
if (stackptr == NULL) { |
339 |
CONSOLE_DEBUG("STACKPTR IS NULL"); |
340 |
return -1; |
341 |
} |
342 |
if (tp == NULL) { |
343 |
CONSOLE_DEBUG("TP IS NULL"); |
344 |
return 2; |
345 |
} |
346 |
if (*stackptr > MAX_TRAP_DEPTH-1) { |
347 |
CONSOLE_DEBUG("TLIST LENGTH = CAPACITY"); |
348 |
return 1; |
349 |
} |
350 |
++(*stackptr); |
351 |
tlist[*stackptr] = tp; |
352 |
return 0; |
353 |
} |
354 |
|
355 |
/* |
356 |
Add a handler to the stack of signal handlers for the given signal. |
357 |
|
358 |
There is a maximum stack limit, so returns 1 if limit exceeded. |
359 |
Returns -1 if stack of signal requested does not exist. |
360 |
Pushing a NULL handler does NOT change anything at all. |
361 |
On a successful return, the handler has been installed and will |
362 |
remain installed until a Asc_SignalHandlerPop or another push. |
363 |
*/ |
364 |
int Asc_SignalHandlerPush(int signum, SigHandlerFn *tp) |
365 |
{ |
366 |
int err; |
367 |
if (tp == NULL) { |
368 |
return 0; |
369 |
} |
370 |
switch (signum) { |
371 |
case SIGFPE: |
372 |
/*ERROR_REPORTER_DEBUG("PUSH SIGFPE");*/ |
373 |
err = push_trap(f_fpe_traps, &f_fpe_top_of_stack, tp); |
374 |
break; |
375 |
case SIGINT: |
376 |
/*ERROR_REPORTER_DEBUG("PUSH SIGINT");*/ |
377 |
err = push_trap(f_int_traps, &f_int_top_of_stack, tp); |
378 |
break; |
379 |
case SIGSEGV: |
380 |
/*ERROR_REPORTER_DEBUG("PUSH SIGSEGV");*/ |
381 |
err = push_trap(f_seg_traps, &f_seg_top_of_stack, tp); |
382 |
break; |
383 |
default: |
384 |
return -1; |
385 |
} |
386 |
if (err != 0) { |
387 |
ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Asc_Signal (%d) stack limit exceeded.",signum); |
388 |
return err; |
389 |
} |
390 |
(void)signal(signum, tp); /* install */ |
391 |
return 0; |
392 |
} |
393 |
|
394 |
/* |
395 |
Returns: 0 -ok, 2 NULL list input, 1 empty list input, |
396 |
-1 mismatched input tp and stack data. |
397 |
*/ |
398 |
static int pop_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){ |
399 |
SigHandlerFn *oldtrap; |
400 |
|
401 |
if ((tlist == NULL) || (stackptr == NULL)) { |
402 |
return 2; |
403 |
} |
404 |
if (*stackptr < 0) { |
405 |
return 1; |
406 |
} |
407 |
oldtrap = tlist[*stackptr]; |
408 |
tlist[*stackptr] = NULL; |
409 |
--(*stackptr); |
410 |
return (-(oldtrap != tp)); |
411 |
} |
412 |
|
413 |
int Asc_SignalHandlerPop(int signum, SigHandlerFn *tp){ |
414 |
int err; |
415 |
switch (signum) { |
416 |
case SIGFPE: |
417 |
/*ERROR_REPORTER_DEBUG("POP SIGFPE");*/ |
418 |
err = pop_trap(f_fpe_traps, &f_fpe_top_of_stack, tp); |
419 |
break; |
420 |
case SIGINT: |
421 |
/*ERROR_REPORTER_DEBUG("POP SIGINT");*/ |
422 |
err = pop_trap(f_int_traps, &f_int_top_of_stack, tp); |
423 |
break; |
424 |
case SIGSEGV: |
425 |
/*ERROR_REPORTER_DEBUG("POP SIGSEGV");*/ |
426 |
err = pop_trap(f_seg_traps, &f_seg_top_of_stack, tp); |
427 |
break; |
428 |
default: |
429 |
CONSOLE_DEBUG("popping invalid signal type (signum = %d)", signum); |
430 |
return -1; |
431 |
} |
432 |
if (err != 0 && tp != NULL) { |
433 |
CONSOLE_DEBUG("stack pop mismatch"); |
434 |
ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Asc_Signal (%d) stack pop mismatch.",signum); |
435 |
return err; |
436 |
} |
437 |
Asc_SignalRecover(TRUE); |
438 |
return 0; |
439 |
} |
440 |
|
441 |
void Asc_SignalTrap(int sigval) { |
442 |
#ifndef NO_SIGNAL_TRAPS |
443 |
switch(sigval) { |
444 |
case SIGFPE: |
445 |
CONSOLE_DEBUG("SIGFPE caught"); |
446 |
FPRESET; |
447 |
longjmp(g_fpe_env,sigval); |
448 |
break; |
449 |
case SIGINT: |
450 |
CONSOLE_DEBUG("SIGINT (Ctrl-C) caught"); |
451 |
longjmp(g_int_env,sigval); |
452 |
break; |
453 |
case SIGSEGV: |
454 |
CONSOLE_DEBUG("SIGSEGV caught"); |
455 |
longjmp(g_seg_env,sigval); |
456 |
break; |
457 |
default: |
458 |
CONSOLE_DEBUG("Installed on unexpected signal (sigval = %d).", sigval); |
459 |
CONSOLE_DEBUG("Returning ... who knows where :-)"); |
460 |
break; |
461 |
} |
462 |
return; |
463 |
#else |
464 |
UNUSED_PARAMETER(sigval); |
465 |
#endif /* NO_SIGNAL_TRAPS */ |
466 |
} |