1 /********************************************************************/
2 /* */
3 /* s7 Seed7 interpreter */
4 /* Copyright (C) 1990 - 2000, 2014, 2016, 2017 Thomas Mertes */
5 /* */
6 /* This program is free software; you can redistribute it and/or */
7 /* modify it under the terms of the GNU General Public License as */
8 /* published by the Free Software Foundation; either version 2 of */
9 /* the License, or (at your option) any later version. */
10 /* */
11 /* This program is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU General Public */
17 /* License along with this program; if not, write to the */
18 /* Free Software Foundation, Inc., 51 Franklin Street, */
19 /* Fifth Floor, Boston, MA 02110-1301, USA. */
20 /* */
21 /* Module: General */
22 /* File: seed7/src/sigutl.c */
23 /* Changes: 1993, 1994 Thomas Mertes */
24 /* Content: Driver shutdown and signal handling. */
25 /* */
26 /********************************************************************/
27
28 #define LOG_FUNCTIONS 0
29 #define VERBOSE_EXCEPTIONS 0
30
31 #include "version.h"
32
33 #include "stdio.h"
34 #include "stdlib.h"
35 #include "signal.h"
36 #include "sys/types.h"
37
38 #include "common.h"
39 #include "fil_drv.h"
40 #include "rtl_err.h"
41
42 #undef EXTERN
43 #define EXTERN
44 #include "sigutl.h"
45
46
47 typedef void (*signalHandlerType) (int signalNum);
48
49 #if HAS_SIGACTION || HAS_SIGNAL
50 static const int normalSignals[] = {SIGABRT, SIGILL, SIGINT, SIGFPE};
51 #endif
52 volatile static suspendInterprType suspendInterpreter;
53
54
55
shutDrivers(void)56 void shutDrivers (void)
57
58 { /* shutDrivers */
59 logSignalFunction(printf("shutDrivers\n"););
60 /* The actual shut functionality is now via atexit(). */
61 fflush(NULL);
62 logSignalFunction(printf("shutDrivers -->\n"););
63 } /* shutDrivers */
64
65
66
67 /**
68 * Determine the name of the given signal 'signalNum'.
69 * @param signalNum Number of the signal.
70 * @return the name of the signal.
71 */
signalName(int signalNum)72 const_cstriType signalName (int signalNum)
73
74 {
75 static char buffer[20];
76 const_cstriType sigName;
77
78 /* signalName */
79 logSignalFunction(printf("signalName(%d)\n", signalNum););
80 switch (signalNum) {
81 case SIGABRT: sigName = "SIGABRT"; break;
82 case SIGFPE: sigName = "SIGFPE"; break;
83 case SIGILL: sigName = "SIGILL"; break;
84 case SIGINT: sigName = "SIGINT"; break;
85 case SIGSEGV: sigName = "SIGSEGV"; break;
86 case SIGTERM: sigName = "SIGTERM"; break;
87 #ifdef SIGALRM
88 case SIGALRM: sigName = "SIGALRM"; break;
89 #endif
90 #ifdef SIGPIPE
91 case SIGPIPE: sigName = "SIGPIPE"; break;
92 #endif
93 default:
94 sprintf(buffer, "%d", signalNum);
95 sigName = buffer;
96 break;
97 } /* switch */
98 logSignalFunction(printf("signalName(%d) --> \"%s\"\n",
99 signalNum, sigName););
100 return sigName;
101 } /* signalName */
102
103
104
105 /**
106 * Trigger the signal SIGFPE such that a debugger can catch it.
107 * The compiler option -e causes that SIGFPE is raised with
108 * triggerSigFpe(), if an uncaught exception occurs. This way a
109 * debugger can handle uncaught Seed7 exceptions.
110 */
triggerSigfpe(void)111 void triggerSigfpe (void)
112
113 {
114 #if DO_SIGFPE_WITH_DIV_BY_ZERO
115 int number;
116 #endif
117
118 /* triggerSigfpe */
119 #if HAS_SIGACTION
120 {
121 struct sigaction sig_act;
122 sigemptyset(&sig_act.sa_mask);
123 sig_act.sa_flags = SA_RESTART;
124 sig_act.sa_handler = SIG_DFL;
125 sigaction(SIGFPE, &sig_act, NULL);
126 }
127 #elif HAS_SIGNAL
128 signal(SIGFPE, SIG_DFL);
129 #endif
130 #if DO_SIGFPE_WITH_DIV_BY_ZERO
131 number = 0;
132 /* Under Windows it is necessary to trigger SIGFPE */
133 /* this way to assure that the debugger can catch it. */
134 printf("%d", 1 / number); /* trigger SIGFPE on purpose */
135 #else
136 raise(SIGFPE);
137 #endif
138 printf("\n*** Continue after SIGFPE.\n");
139 } /* triggerSigfpe */
140
141
142
143 /**
144 * Dialog to decide how to continue after a signal has been received.
145 * This function might be called from a signal handler. Note that this
146 * function does things that are beyond the specification of what
147 * signal handlers are allowed to do. Signal handlers should be left
148 * quickly and several functions (including I/O functions) should never
149 * be called from them. Waiting in a signal handler for the user to
150 * respond violates that. Besides that this function works under Linux
151 * BSD and Windows with various run-time libraries.
152 */
signalDecision(int signalNum,boolType inHandler)153 static boolType signalDecision (int signalNum, boolType inHandler)
154
155 {
156 int ch;
157 boolType sigintReceived;
158 int position;
159 char buffer[10];
160 long unsigned int exceptionNum;
161 boolType resume = FALSE;
162
163 /* signalDecision */
164 logSignalFunction(printf("signalDecision(%d, %d)\n",
165 signalNum, inHandler););
166 printf("\n*** SIGNAL %s RAISED\n"
167 "\n*** The following commands are possible:\n"
168 " RETURN Continue\n"
169 " * Terminate\n"
170 " / Trigger SIGFPE\n"
171 " !n Raise exception with number (e.g.: !1 raises MEMORY_ERROR)\n",
172 signalName(signalNum));
173 if (suspendInterpreter != NULL) {
174 printf(" c Suspend the program\n");
175 } /* if */
176 if (inHandler) {
177 do {
178 ch = fgetc(stdin);
179 } while (ch == ' ');
180 } else {
181 do {
182 ch = readCharChkCtrlC(stdin, &sigintReceived);
183 } while (sigintReceived || ch == ' ');
184 } /* if */
185 if (ch == '*') {
186 shutDrivers();
187 exit(1);
188 } else if (ch == '/') {
189 triggerSigfpe();
190 } else if (suspendInterpreter != NULL && ch == 'c') {
191 suspendInterpreter(signalNum);
192 } else if (ch == '!') {
193 position = 0;
194 while ((ch = fgetc(stdin)) >= (int) ' ' && ch <= (int) '~' && position < 4) {
195 buffer[position] = (char) ch;
196 position++;
197 } /* while */
198 buffer[position] = '\0';
199 if (position > 0 && buffer[0] >= '0' && buffer[0] <= '9') {
200 exceptionNum = strtoul(buffer, NULL, 10);
201 raise_error((int) exceptionNum);
202 } /* if */
203 } else {
204 resume = TRUE;
205 } /* if */
206 while (ch != EOF && ch != '\n') {
207 ch = fgetc(stdin);
208 } /* while */
209 return resume;
210 } /* signalDecision */
211
212
213
214 #if HAS_SIGACTION || HAS_SIGNAL
215 /**
216 * Signal handler that is used if tracing signals as been activated.
217 * Tracing signals is activated in interpreter and compiler with the
218 * option -ts. This signal handler is used for normalSignals
219 * (e.g.: SIGABRT, SIGILL, SIGINT, SIGFPE).
220 */
handleTracedSignals(int signalNum)221 static void handleTracedSignals (int signalNum)
222
223 { /* handleTracedSignals */
224 #if defined SIGALRM && !HAS_SIGACTION
225 signal(SIGALRM, SIG_IGN);
226 #endif
227 #if DIALOG_IN_SIGNAL_HANDLER
228 (void) signalDecision(signalNum, TRUE);
229 #else
230 if (suspendInterpreter != NULL) {
231 suspendInterpreter(signalNum);
232 } /* if */
233 #endif
234 #if SIGNAL_RESETS_HANDLER
235 signal(signalNum, handleTracedSignals);
236 #endif
237 } /* handleTracedSignals */
238
239
240
241 /**
242 * Signal handler for the signal SIGFPE.
243 */
handleNumericError(int signalNum)244 static void handleNumericError (int signalNum)
245
246 { /* handleNumericError */
247 #if SIGNAL_RESETS_HANDLER
248 signal(signalNum, handleNumericError);
249 #endif
250 raise_error(NUMERIC_ERROR);
251 } /* handleNumericError */
252
253
254
255 #if OVERFLOW_SIGNAL
256 /**
257 * Signal handler for the OVERFLOW_SIGNAL (SIGILL, SIGABRT or SIGTRAP).
258 */
handleOverflowError(int signalNum)259 static void handleOverflowError (int signalNum)
260
261 {
262 #if SIGNAL_RESETS_HANDLER
263 signal(signalNum, handleOverflowError);
264 #endif
265 raise_error(OVERFLOW_ERROR);
266 }
267 #endif
268
269
270
271 /**
272 * Signal handler for signals that terminate the program.
273 * This signal handler is used for SIGTERM and for normalSignals
274 * (e.g.: SIGABRT, SIGILL, SIGINT, SIGFPE).
275 */
handleTermSignal(int signalNum)276 static void handleTermSignal (int signalNum)
277
278 { /* handleTermSignal */
279 printf("\n*** SIGNAL %s RAISED\n"
280 "\n*** Program terminated.\n", signalName(signalNum));
281 shutDrivers();
282 exit(1);
283 } /* handleTermSignal */
284
285
286
287 /**
288 * Signal handler for the signal SIGSEGV.
289 */
handleSegvSignal(int signalNum)290 static void handleSegvSignal (int signalNum)
291
292 { /* handleSegvSignal */
293 shutDrivers();
294 printf("\n*** SIGNAL SEGV RAISED\n"
295 "\n*** Program terminated.\n");
296 #if HAS_SIGACTION
297 {
298 struct sigaction sigAct;
299 sigemptyset(&sigAct.sa_mask);
300 sigAct.sa_flags = SA_RESTART;
301 sigAct.sa_handler = SIG_DFL;
302 sigaction(SIGABRT, &sigAct, NULL);
303 }
304 #elif HAS_SIGNAL
305 signal(SIGABRT, SIG_DFL);
306 #endif
307 abort();
308 } /* handleSegvSignal */
309
310
311
312 /**
313 * Initialize the signal handlers.
314 * @param handleSignals Specifies if signals should be handled at all.
315 * @param traceSignals Specifies if signals should trigger a dialog at
316 * the console.
317 * @param overflowSigError Specifies if an OVERFLOW_SIGNAL should
318 * raise OVERFLOW_ERROR.
319 * @param fpeNumericError Specifies if SIGFPE should raise NUMERIC_ERROR.
320 */
321 #if HAS_SIGACTION
setupSignalHandlers(boolType handleSignals,boolType traceSignals,boolType overflowSigError,boolType fpeNumericError,suspendInterprType suspendInterpr)322 void setupSignalHandlers (boolType handleSignals,
323 boolType traceSignals, boolType overflowSigError,
324 boolType fpeNumericError, suspendInterprType suspendInterpr)
325
326 {
327 unsigned int pos;
328 int signalNum;
329 struct sigaction sigAct;
330 boolType okay = TRUE;
331
332 /* setupSignalHandlers */
333 logFunction(printf("setupSignalHandlers(%d, %d, %d, %d, " FMT_U_MEM ")\n",
334 handleSignals, traceSignals, overflowSigError,
335 fpeNumericError, (memSizeType) suspendInterpr););
336 suspendInterpreter = suspendInterpr;
337 if (handleSignals) {
338 sigemptyset(&sigAct.sa_mask);
339 #ifdef SIGALRM
340 sigaddset(&sigAct.sa_mask, SIGALRM);
341 #endif
342 sigAct.sa_flags = SA_RESTART;
343 for (pos = 0; pos < sizeof(normalSignals) / sizeof(int); pos++) {
344 signalNum = normalSignals[pos];
345 #if OVERFLOW_SIGNAL
346 if (signalNum == OVERFLOW_SIGNAL && overflowSigError) {
347 sigAct.sa_handler = handleOverflowError;
348 } else
349 #endif
350 if (signalNum == SIGFPE && fpeNumericError) {
351 sigAct.sa_handler = handleNumericError;
352 } else if (traceSignals) {
353 sigAct.sa_handler = handleTracedSignals;
354 } else {
355 sigAct.sa_handler = handleTermSignal;
356 } /* if */
357 okay = okay && sigaction(signalNum, &sigAct, NULL) == 0;
358 } /* for */
359 #if OVERFLOW_SIGNAL && defined SIGTRAP && OVERFLOW_SIGNAL == SIGTRAP
360 if (overflowSigError) {
361 sigAct.sa_handler = handleOverflowError;
362 okay = okay && sigaction(SIGTRAP, &sigAct, NULL) == 0;
363 } else if (traceSignals) {
364 sigAct.sa_handler = handleTracedSignals;
365 okay = okay && sigaction(SIGTRAP, &sigAct, NULL) == 0;
366 } /* if */
367 #endif
368 sigAct.sa_handler = handleTermSignal;
369 okay = okay && sigaction(SIGTERM, &sigAct, NULL) == 0;
370 if (traceSignals) {
371 sigAct.sa_handler = handleSegvSignal;
372 } else {
373 sigAct.sa_handler = SIG_DFL;
374 } /* if */
375 okay = okay && sigaction(SIGSEGV, &sigAct, NULL) == 0;
376 #ifdef SIGPIPE
377 sigAct.sa_handler = SIG_IGN;
378 okay = okay && sigaction(SIGPIPE, &sigAct, NULL) == 0;
379 #endif
380 } /* if */
381 if (!okay) {
382 printf("\n*** Activating signal handlers failed.\n");
383 } /* if */
384 logFunction(printf("setupSignalHandlers -->\n"););
385 } /* setupSignalHandlers */
386
387 #elif HAS_SIGNAL
388
389
390
setupSignalHandlers(boolType handleSignals,boolType traceSignals,boolType overflowSigError,boolType fpeNumericError,suspendInterprType suspendInterpr)391 void setupSignalHandlers (boolType handleSignals,
392 boolType traceSignals, boolType overflowSigError,
393 boolType fpeNumericError, suspendInterprType suspendInterpr)
394
395 {
396 int pos;
397 int signalNum;
398 signalHandlerType sigHandler;
399 boolType okay = TRUE;
400
401 /* setupSignalHandlers */
402 logFunction(printf("setupSignalHandlers(%d, %d, %d, %d, " FMT_U_MEM ")\n",
403 handleSignals, traceSignals, overflowSigError,
404 fpeNumericError, (memSizeType) suspendInterpr););
405 suspendInterpreter = suspendInterpr;
406 if (handleSignals) {
407 for (pos = 0; pos < sizeof(normalSignals) / sizeof(int); pos++) {
408 signalNum = normalSignals[pos];
409 #if OVERFLOW_SIGNAL
410 if (signalNum == OVERFLOW_SIGNAL && overflowSigError) {
411 sigHandler = handleOverflowError;
412 } else
413 #endif
414 if (signalNum == SIGFPE && fpeNumericError) {
415 sigHandler = handleNumericError;
416 } else if (traceSignals) {
417 sigHandler = handleTracedSignals;
418 } else {
419 sigHandler = handleTermSignal;
420 } /* if */
421 okay = okay && signal(signalNum, sigHandler) != SIG_ERR;
422 } /* for */
423 #if OVERFLOW_SIGNAL && defined SIGTRAP && OVERFLOW_SIGNAL == SIGTRAP
424 if (overflowSigError) {
425 okay = okay && signal(SIGTRAP, handleOverflowError) != SIG_ERR;
426 } else if (traceSignals) {
427 okay = okay && signal(SIGTRAP, handleTracedSignals) != SIG_ERR;
428 } /* if */
429 #endif
430 okay = okay && signal(SIGTERM, handleTermSignal) != SIG_ERR;
431 if (traceSignals) {
432 sigHandler = handleSegvSignal;
433 } else {
434 sigHandler = SIG_DFL;
435 } /* if */
436 okay = okay && signal(SIGSEGV, sigHandler) != SIG_ERR;
437 #ifdef SIGPIPE
438 signal(SIGPIPE, SIG_IGN);
439 #endif
440 } /* if */
441 if (!okay) {
442 printf("\n*** Activating signal handlers failed.\n");
443 } /* if */
444 logFunction(printf("setupSignalHandlers -->\n"););
445 } /* setupSignalHandlers */
446
447 #endif
448 #else
449
450
451
setupSignalHandlers(boolType handleSignals,boolType traceSignals,boolType overflowSigError,boolType fpeNumericError,suspendInterprType suspendInterpr)452 void setupSignalHandlers (boolType handleSignals,
453 boolType traceSignals, boolType overflowSigError,
454 boolType fpeNumericError, suspendInterprType suspendInterpr)
455
456 { /* setupSignalHandlers */
457 logFunction(printf("setupSignalHandlers(%d, %d, %d, %d, " FMT_U_MEM ")\n",
458 handleSignals, traceSignals, overflowSigError,
459 fpeNumericError, (memSizeType) suspendInterpr););
460 logFunction(printf("setupSignalHandlers -->\n"););
461 } /* setupSignalHandlers */
462 #endif
463
464
465
466 /**
467 * Determine the current signal handler of the given signal 'signalNum'.
468 * @param signalNum Number of the signal.
469 * @return signal handler that corresponds to 'signalNum', or
470 * SIG_ERR if no signal handler could be found.
471 */
getCurrentSignalHandler(int signalNum)472 static signalHandlerType getCurrentSignalHandler (int signalNum)
473
474 {
475 #if HAS_SIGACTION
476 struct sigaction oldAction;
477 #endif
478 signalHandlerType currentHandler;
479
480 /* getCurrentSignalHandler */
481 logFunction(printf("getCurrentSignalHandler(%d)\n", signalNum););
482 #if HAS_SIGACTION
483 if (likely(sigaction(signalNum, NULL, &oldAction) == 0)) {
484 currentHandler = oldAction.sa_handler;
485 #elif HAS_SIGNAL
486 currentHandler = signal(signalNum, SIG_IGN);
487 if (likely(currentHandler != SIG_ERR) &&
488 signal(signalNum, currentHandler) != SIG_ERR) {
489 #endif
490 #if HAS_SIGACTION || HAS_SIGNAL
491 } else {
492 logError(printf("getCurrentSignalHandler(%d) failed.\n", signalNum););
493 currentHandler = SIG_ERR;
494 } /* if */
495 #else
496 currentHandler = SIG_ERR;
497 #endif
498 logFunction(printf("getCurrentSignalHandler(%d) --> " FMT_U_MEM "\n",
499 signalNum, (memSizeType) currentHandler););
500 return currentHandler;
501 } /* getCurrentSignalHandler */
502
503
504
505 /**
506 * Call the current signal handler without raising the signal.
507 * This function is called if ctrl-c has been pressed.
508 * If signal tracing is switched on signalDecision() is called
509 * to determine if the program should resume.
510 * @param signalNum Number of the signal.
511 * @return TRUE if the program should resume, or
512 * FALSE if the program should not resume.
513 */
514 boolType callSignalHandler (int signalNum)
515
516 {
517 signalHandlerType currentHandler;
518 boolType resume = FALSE;
519
520 /* callSignalHandler */
521 logFunction(printf("callSignalHandler(%d)\n", signalNum););
522 currentHandler = getCurrentSignalHandler(signalNum);
523 #if HAS_SIGACTION || HAS_SIGNAL
524 if (currentHandler == handleTracedSignals) {
525 resume = signalDecision(signalNum, FALSE);
526 } else
527 #endif
528 if (currentHandler == SIG_DFL) {
529 raise(signalNum);
530 } else if (currentHandler != SIG_IGN && currentHandler != SIG_ERR) {
531 currentHandler(signalNum);
532 } /* if */
533 return resume;
534 } /* callSignalHandler */
535