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