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