71a50dfa4959fa0250eb6e4a21e58dc794ce834d
[scilab.git] / scilab / modules / core / src / c / signal_mgmt.c
1 /*
2   Copyright (C) 2006  EDF - Code Saturne
3   Copyright (C) 2001 - DIGITEO - Sylvestre LEDRU. Adapted for Scilab
4   Copyright (C) 2016 - Scilab Enterprises - Clement DAVID
5
6   This library is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Lesser General Public
8   License as published by the Free Software Foundation; either
9   version 2.1 of the License, or (at your option) any later version.
10
11   This library 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 GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with this library; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <signal.h>
27 #include <string.h>
28 #include <time.h>
29 #include <libintl.h>
30 #include <pthread.h>
31
32 #include <setjmp.h>
33
34 #include <sys/types.h>          /* getpid */
35 #include <sys/time.h>           /* gettimeofday */
36 #include <unistd.h>             /* gethostname */
37
38 #include "csignal.h"
39 #include "localization.h"
40 #include "backtrace.h"
41 #include "signal_mgmt.h"
42 #include "Scierror.h"
43 #include "suspendProcess.h"
44 #include "configvariable_interface.h"
45 #include "backtrace_print.h"
46 #include "cliDisplayManagement.h"
47 #include "initConsoleMode.h"
48 #include "exit_status.hxx"
49
50 jmp_buf ScilabJmpEnv;
51
52 /*----------------------------------------------------------------------------
53  * Handle a fatal signal (such as SIGFPE or SIGSEGV)
54  *----------------------------------------------------------------------------*/
55 #define HOSTFORMAT "[%s:%05d] "
56 static void sig_fatal(int signum, siginfo_t * info, void *p)
57 {
58     char *si_code_str = "";
59
60     int ret, i;
61
62     char print_buffer[2048];
63
64     int size = sizeof(print_buffer);
65
66     char *tmp = print_buffer;
67
68     char stacktrace_hostname[64];
69
70     char* bt;
71
72     gethostname(stacktrace_hostname, sizeof(stacktrace_hostname));
73     stacktrace_hostname[sizeof(stacktrace_hostname) - 1] = '\0';
74     /* to keep these somewhat readable, only print the machine name */
75     for (i = 0;
76             i < (int)sizeof(stacktrace_hostname) && stacktrace_hostname[i] != '\0';
77             ++i)
78     {
79         if (stacktrace_hostname[i] == '.')
80         {
81             stacktrace_hostname[i] = '\0';
82             break;
83         }
84     }
85
86     fflush(stdout);
87     memset(print_buffer, 0, sizeof(print_buffer));
88
89     /* This list comes from OpenMPI sources */
90 #ifdef HAVE_STRSIGNAL
91     {
92         char* str;
93         str = strsignal(signum);
94         ret = snprintf(tmp, size, HOSTFORMAT "Signal: %s (%d)\n", stacktrace_hostname, getpid(), str, signum);
95     }
96 #else
97     ret = snprintf(tmp, size, HOSTFORMAT "Signal: %d\n", stacktrace_hostname, getpid(), signum);
98 #endif
99     tmp += ret;
100     size -= ret;
101
102     if (NULL != info)
103     {
104         switch (signum)
105         {
106             case SIGILL:
107                 switch (info->si_code)
108                 {
109 #ifdef ILL_ILLOPC
110                     case ILL_ILLOPC:
111                         si_code_str = "Illegal opcode";
112                         break;
113 #endif
114 #ifdef ILL_ILLOPN
115                     case ILL_ILLOPN:
116                         si_code_str = "Illegal operand";
117                         break;
118 #endif
119 #ifdef ILL_ILLADR
120                     case ILL_ILLADR:
121                         si_code_str = "Illegal addressing mode";
122                         break;
123 #endif
124 #ifdef ILL_ILLTRP
125                     case ILL_ILLTRP:
126                         si_code_str = "Illegal trap";
127                         break;
128 #endif
129 #ifdef ILL_PRVOPC
130                     case ILL_PRVOPC:
131                         si_code_str = "Privileged opcode";
132                         break;
133 #endif
134 #ifdef ILL_PRVREG
135                     case ILL_PRVREG:
136                         si_code_str = "Privileged register";
137                         break;
138 #endif
139 #ifdef ILL_COPROC
140                     case ILL_COPROC:
141                         si_code_str = "Coprocessor error";
142                         break;
143 #endif
144 #ifdef ILL_BADSTK
145                     case ILL_BADSTK:
146                         si_code_str = "Internal stack error";
147                         break;
148 #endif
149                 }
150                 break;
151             case SIGFPE:
152                 switch (info->si_code)
153                 {
154 #ifdef FPE_INTDIV
155                     case FPE_INTDIV:
156                         si_code_str = "Integer divide-by-zero";
157                         break;
158 #endif
159 #ifdef FPE_INTOVF
160                     case FPE_INTOVF:
161                         si_code_str = "Integer overflow";
162                         break;
163 #endif
164                     case FPE_FLTDIV:
165                         si_code_str = "Floating point divide-by-zero";
166                         break;
167                     case FPE_FLTOVF:
168                         si_code_str = "Floating point overflow";
169                         break;
170                     case FPE_FLTUND:
171                         si_code_str = "Floating point underflow";
172                         break;
173 #ifdef FPE_FLTRES
174                     case FPE_FLTRES:
175                         si_code_str = "Floating point inexact result";
176                         break;
177 #endif
178 #ifdef FBE_FLTINV
179                     case FPE_FLTINV:
180                         si_code_str = "Invalid floating point operation";
181                         break;
182 #endif
183 #ifdef FPE_FLTSUB
184                     case FPE_FLTSUB:
185                         si_code_str = "Subscript out of range";
186                         break;
187 #endif
188                 }
189                 break;
190             case SIGSEGV:
191                 switch (info->si_code)
192                 {
193 #ifdef SEGV_MAPERR
194                     case SEGV_MAPERR:
195                         si_code_str = "Address not mapped";
196                         break;
197 #endif
198 #ifdef SEGV_ACCERR
199                     case SEGV_ACCERR:
200                         si_code_str = "Invalid permissions";
201                         break;
202 #endif
203                 }
204                 break;
205             case SIGBUS:
206                 switch (info->si_code)
207                 {
208 #ifdef BUS_ADRALN
209                     case BUS_ADRALN:
210                         si_code_str = "Invalid address alignment";
211                         break;
212 #endif
213 #ifdef BUSADRERR
214                     case BUS_ADRERR:
215                         si_code_str = "Non-existent physical address";
216                         break;
217 #endif
218 #ifdef BUS_OBJERR
219                     case BUS_OBJERR:
220                         si_code_str = "Objet-specific hardware error";
221                         break;
222 #endif
223                 }
224                 break;
225             case SIGTRAP:
226                 switch (info->si_code)
227                 {
228 #ifdef TRAP_BRKPT
229                     case TRAP_BRKPT:
230                         si_code_str = "Process breakpoint";
231                         break;
232 #endif
233 #ifdef TRAP_TRACE
234                     case TRAP_TRACE:
235                         si_code_str = "Process trace trap";
236                         break;
237 #endif
238                 }
239                 break;
240             case SIGCHLD:
241                 switch (info->si_code)
242                 {
243 #ifdef CLD_EXITED
244                     case CLD_EXITED:
245                         si_code_str = "Child has exited";
246                         break;
247 #endif
248 #ifdef CLD_KILLED
249                     case CLD_KILLED:
250                         si_code_str = "Child has terminated abnormally and did not create a core file";
251                         break;
252 #endif
253 #ifdef CLD_DUMPED
254                     case CLD_DUMPED:
255                         si_code_str = "Child has terminated abnormally and created a core file";
256                         break;
257 #endif
258 #ifdef CLD_WTRAPPED
259                     case CLD_TRAPPED:
260                         si_code_str = "Traced child has trapped";
261                         break;
262 #endif
263 #ifdef CLD_STOPPED
264                     case CLD_STOPPED:
265                         si_code_str = "Child has stopped";
266                         break;
267 #endif
268 #ifdef CLD_CONTINUED
269                     case CLD_CONTINUED:
270                         si_code_str = "Stopped child has continued";
271                         break;
272 #endif
273                 }
274                 break;
275 #ifdef SIGPOLL
276             case SIGPOLL:
277                 switch (info->si_code)
278                 {
279 #ifdef POLL_IN
280                     case POLL_IN:
281                         si_code_str = "Data input available";
282                         break;
283 #endif
284 #ifdef POLL_OUT
285                     case POLL_OUT:
286                         si_code_str = "Output buffers available";
287                         break;
288 #endif
289 #ifdef POLL_MSG
290                     case POLL_MSG:
291                         si_code_str = "Input message available";
292                         break;
293 #endif
294 #ifdef POLL_ERR
295                     case POLL_ERR:
296                         si_code_str = "I/O error";
297                         break;
298 #endif
299 #ifdef POLL_PRI
300                     case POLL_PRI:
301                         si_code_str = "High priority input available";
302                         break;
303 #endif
304 #ifdef POLL_HUP
305                     case POLL_HUP:
306                         si_code_str = "Device disconnected";
307                         break;
308 #endif
309                 }
310                 break;
311 #endif /* SIGPOLL */
312             default:
313                 switch (info->si_code)
314                 {
315 #ifdef SI_ASYNCNL
316                     case SI_ASYNCNL:
317                         si_code_str = "SI_ASYNCNL";
318                         break;
319 #endif
320 #ifdef SI_SIGIO
321                     case SI_SIGIO:
322                         si_code_str = "Queued SIGIO";
323                         break;
324 #endif
325 #ifdef SI_ASYNCIO
326                     case SI_ASYNCIO:
327                         si_code_str = "Asynchronous I/O request completed";
328                         break;
329 #endif
330 #ifdef SI_MESGQ
331                     case SI_MESGQ:
332                         si_code_str = "Message queue state changed";
333                         break;
334 #endif
335                     case SI_TIMER:
336                         si_code_str = "Timer expiration";
337                         break;
338                     case SI_QUEUE:
339                         si_code_str = "Sigqueue() signal";
340                         break;
341                     case SI_USER:
342                         si_code_str = "User function (kill, sigsend, abort, etc.)";
343                         break;
344 #ifdef SI_KERNEL
345                     case SI_KERNEL:
346                         si_code_str = "Kernel signal";
347                         break;
348 #endif
349 #ifdef SI_UNDEFINED
350                     case SI_UNDEFINED:
351                         si_code_str = "Undefined code";
352                         break;
353 #endif
354                 }
355         }
356
357         /* print signal errno information */
358         if (0 != info->si_errno)
359         {
360             ret = snprintf(tmp, size, HOSTFORMAT "Associated errno: %s (%d)\n",
361                            stacktrace_hostname, getpid(), strerror(info->si_errno), info->si_errno);
362             tmp += ret;
363             size -= ret;
364         }
365
366         ret = snprintf(tmp, size, HOSTFORMAT "Signal code: %s (%d)\n", stacktrace_hostname, getpid(), si_code_str, info->si_code);
367         tmp += ret;
368         size -= ret;
369
370         switch (signum)
371         {
372             case SIGILL:
373             case SIGFPE:
374             case SIGSEGV:
375             case SIGBUS:
376             {
377                 ret = snprintf(tmp, size, HOSTFORMAT "Failing at address: %p\n", stacktrace_hostname, getpid(), info->si_addr);
378                 break;
379             }
380             case SIGCHLD:
381             {
382                 snprintf(tmp, size, HOSTFORMAT "Sending PID: %d, Sending UID: %d, Status: %d\n",
383                          stacktrace_hostname, getpid(), info->si_pid, info->si_uid, info->si_status);
384                 break;
385             }
386 #ifdef SIGPOLL
387             case SIGPOLL:
388             {
389 #ifdef HAVE_SIGINFO_T_SI_FD
390                 snprintf(tmp, size, HOSTFORMAT "Band event: %ld, File Descriptor : %d\n",
391                          stacktrace_hostname, getpid(), info->si_band, info->si_fd);
392 #elif HAVE_SIGINFO_T_SI_BAND
393                 snprintf(tmp, size, HOSTFORMAT "Band event: %ld\n", stacktrace_hostname, getpid(), info->si_band);
394 #endif
395                 break;
396             }
397 #endif
398         }
399     }
400     else
401     {
402         snprintf(tmp, size, HOSTFORMAT "siginfo is NULL, additional information unavailable\n", stacktrace_hostname, getpid());
403     }
404
405     // 4 is to ignore the first 4 functions
406     bt = backtrace_print(4, 1);
407     Scierror(42,
408              _("A fatal error has been detected by Scilab.\nPlease check your user-defined functions (or external module ones) should they appear in the stack trace.\nOtherwise you can report a bug on %s with:\n * a sample code which reproduces the issue\n * the result of [a, b] = getdebuginfo()\n * the following information:\n%s %s\n"),
409              PACKAGE_BUGREPORT, print_buffer, bt);
410
411     free(bt);
412
413     if (getScilabMode() == SCILAB_NWNI || getScilabMode() == SCILAB_NW)
414     {
415         /* Reset termcaps and Characters display. */
416         setAttr(ATTR_RESET);
417         setCharDisplay(DISP_RESET);
418     }
419
420     longjmp(ScilabJmpEnv, HUGE_ERROR);
421 }
422
423 void base_error_init(void)
424 {
425     struct sigaction act;
426     int j;
427     struct sigaction ToSuspend;
428     struct sigaction ToContinue;
429     int signals[] =
430     {
431 #ifdef SIGABRT
432         SIGABRT,
433 #endif
434 #ifdef SIGBUS
435         SIGBUS,
436 #endif
437 #ifdef SIGFPE
438         SIGFPE,
439 #endif
440 #ifdef SIGFPE2
441         SIGFPE,
442 #endif
443 #ifdef SIGILL
444         SIGILL,
445 #endif
446 #ifdef SIGSEGV
447         SIGSEGV,
448 #endif
449 #ifdef SIGPOLL
450         SIGPOLL,
451 #endif
452         -1
453     };
454
455     /* Initialise Suspend Signal (CTRL-Z) */
456     ToSuspend.sa_handler = suspendProcess;
457     ToSuspend.sa_flags = 0;
458     sigemptyset(&ToSuspend.sa_mask);
459     sigaction(SIGTSTP, &ToSuspend, NULL);
460     /* Initialise Continue Signal (fg) */
461     ToContinue.sa_handler = continueProcess;
462     ToContinue.sa_flags = 0;
463     sigemptyset(&ToContinue.sa_mask);
464     sigaction(SIGCONT, &ToContinue, NULL);
465     /* Signal handlers */
466     csignal();
467     memset(&act, 0, sizeof(act));
468     act.sa_sigaction = sig_fatal;
469     act.sa_flags = SA_SIGINFO;
470 #ifdef SA_ONESHOT
471     act.sa_flags |= SA_ONESHOT;
472 #else
473     act.sa_flags |= SA_RESETHAND;
474 #endif
475     sigemptyset(&act.sa_mask);
476
477     for (j = 0; signals[j] != -1; ++j)
478     {
479         if (0 != sigaction(signals[j], &act, NULL))
480         {
481             fprintf(stderr, "Could not set handler for signal %d\n", signals[j]);
482         }
483     }
484
485 #ifdef HAVE_STRSIGNAL
486     // initialize the glibc internal string representation.
487     strsignal(SIGABRT);
488 #endif
489 }
490
491 static void* watchdog_thread(void* arg)
492 {
493     long timeoutDelay = (long) arg;
494
495     pthread_mutex_t watchdog_mutex;
496     pthread_cond_t dummy_condition;
497     struct timeval tv;
498     struct timespec abstime;
499
500     if (pthread_mutex_init(&watchdog_mutex, NULL) != 0)
501     {
502         return NULL;
503     }
504
505     if (pthread_cond_init(&dummy_condition, NULL) != 0)
506     {
507         pthread_mutex_destroy(&watchdog_mutex);
508         return NULL;
509     }
510
511     if (gettimeofday(&tv, NULL) != 0)
512     {
513         pthread_cond_destroy(&dummy_condition);
514         pthread_mutex_destroy(&watchdog_mutex);
515         return NULL;
516     }
517
518     memset(&abstime, 0, sizeof(struct timespec));
519     abstime.tv_sec = tv.tv_sec + timeoutDelay;
520
521     pthread_mutex_lock(&watchdog_mutex);
522     while (1)
523     {
524         if (pthread_cond_timedwait(&dummy_condition, &watchdog_mutex, &abstime) == ETIMEDOUT)
525         {
526             /*
527              * Send a SIGABRT to ensure process termination, if used with the signal
528              * trap a backtrace might be displayed.
529              */
530             kill(getpid(), SIGABRT);
531         }
532     }
533     pthread_mutex_unlock(&watchdog_mutex);
534     return NULL;
535 }
536
537 void timeout_process_after(int timeoutDelay)
538 {
539     pthread_t watchdog;
540
541     // Spawn a watchdog thread as POSIX timer API is not available on MacOS X
542     if (pthread_create(&watchdog, NULL, watchdog_thread, (void*) (long) timeoutDelay) != 0)
543     {
544         return;
545     }
546 }
547
548 /*--------------------------------------------------------------------------*/