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