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