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