1 /****************************************
2 * Computer Algebra System SINGULAR *
3 ****************************************/
4 /*
5 * ABSTRACT - interupt handling
6 */
7
8 #ifndef _GNU_SOURCE
9 #define _GNU_SOURCE
10 #endif
11
12 #include "kernel/mod2.h"
13
14 #include "reporter/si_signals.h"
15 #include "Singular/fevoices.h"
16
17 #include "misc/options.h"
18 #include "Singular/tok.h"
19 #include "Singular/ipshell.h"
20 #include "Singular/cntrlc.h"
21 #include "Singular/feOpt.h"
22 #include "Singular/misc_ip.h"
23 #include "Singular/links/silink.h"
24 #include "Singular/links/ssiLink.h"
25
26 #ifdef HAVE_NTL
27 #include <NTL/version.h>
28 #include <NTL/tools.h>
29 #ifdef NTL_CLIENT
30 NTL_CLIENT
31 #endif
32 #endif
33
34 /* undef, if you don't want GDB to come up on error */
35
36 #define CALL_GDB
37
38 #if defined(__OPTIMIZE__) && defined(CALL_GDB)
39 #undef CALL_GDB
40 #endif
41
42 #ifdef TIME_WITH_SYS_TIME
43 #include <time.h>
44 #ifdef HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #endif
47 #else
48 #ifdef HAVE_SYS_TIME_H
49 #include <sys/time.h>
50 #else
51 #include <time.h>
52 #endif
53 #endif
54 #ifdef HAVE_SYS_TIMES_H
55 #include <sys/times.h>
56 #endif
57
58 #define INTERACTIVE 0
59 #define STACK_TRACE 1
60
61 #ifdef CALL_GDB
62 static void debug (int);
63 static void debug_stop (char *const*args);
64 #endif
65 #ifndef __OPTIMIZE__
66 static void stack_trace (char *const*args);
67 #endif
68
69 VAR si_link pipeLastLink=NULL;
70 VAR BOOLEAN singular_in_batchmode=FALSE;
71
sig_pipe_hdl(int)72 void sig_pipe_hdl(int /*sig*/)
73 {
74 if (pipeLastLink!=NULL)
75 {
76 slClose(pipeLastLink);
77 pipeLastLink=NULL;
78 WerrorS("pipe failed");
79 }
80 }
81
82 VAR volatile BOOLEAN do_shutdown = FALSE;
83 VAR volatile int defer_shutdown = 0;
84
sig_term_hdl(int)85 void sig_term_hdl(int /*sig*/)
86 {
87 do_shutdown = TRUE;
88 if (!defer_shutdown)
89 {
90 m2_end(1);
91 }
92 }
93
94 /*---------------------------------------------------------------------*
95 * File scope Variables (Variables shared by several functions in
96 * the same file )
97 *
98 *---------------------------------------------------------------------*/
99 /* data */
100 VAR jmp_buf si_start_jmpbuf;
101 VAR int siRandomStart;
102 VAR short si_restart=0;
103
104 typedef void (*si_hdl_typ)(int);
105
106
107 /*0 implementation*/
108 /*---------------------------------------------------------------------*
109 * Functions declarations
110 *
111 *---------------------------------------------------------------------*/
112 void sigint_handler(int /*sig*/);
113
114 si_hdl_typ si_set_signal ( int sig, si_hdl_typ signal_handler);
115
116 /*---------------------------------------------------------------------*/
117 /**
118 * @brief meta function for binding a signal to an handler
119
120 @param[in] sig Signal number
121 @param[in] signal_handler Pointer to signal handler
122
123 @return value of signal()
124 **/
125 /*---------------------------------------------------------------------*/
si_set_signal(int sig,si_hdl_typ signal_handler)126 si_hdl_typ si_set_signal ( int sig, si_hdl_typ signal_handler)
127 {
128 #if 0
129 si_hdl_typ retval=signal (sig, (si_hdl_typ)signal_handler);
130 if (retval == SIG_ERR)
131 {
132 fprintf(stderr, "Unable to init signal %d ... exiting...\n", sig);
133 }
134 si_siginterrupt(sig, 0);
135 /*system calls will be restarted if interrupted by the specified
136 * signal sig. This is the default behavior in Linux.
137 */
138 #else
139 struct sigaction new_action,old_action;
140 memset(&new_action, 0, sizeof(struct sigaction));
141
142 /* Set up the structure to specify the new action. */
143 new_action.sa_handler = signal_handler;
144 if (sig==SIGINT)
145 sigemptyset (&new_action.sa_mask);
146 else
147 new_action.sa_flags = SA_RESTART;
148
149 int r=si_sigaction (sig, &new_action, &old_action);
150 si_hdl_typ retval=(si_hdl_typ)old_action.sa_handler;
151 if (r == -1)
152 {
153 fprintf(stderr, "Unable to init signal %d ... exiting...\n", sig);
154 retval=SIG_ERR;
155 }
156 #endif
157 return retval;
158 } /* si_set_signal */
159
160
161 /*---------------------------------------------------------------------*/
162 #if defined(__linux__) && (defined(__i386) || defined(__amd64))
163 /*2---------------------------------------------------------------------*/
164 /**
165 * @brief signal handler for run time errors, linux/i386, x86_64 version
166
167 @param[in] sig
168 @param[in] s
169 **/
170 /*---------------------------------------------------------------------*/
sigsegv_handler(int sig,sigcontext s)171 void sigsegv_handler(int sig, sigcontext s)
172 {
173 fprintf(stderr,"Singular : signal %d (v: %d):\n",sig,SINGULAR_VERSION);
174 if (sig!=SIGINT)
175 {
176 fprintf(stderr,"current line:>>%s<<\n",my_yylinebuf);
177 fprintf(stderr,"Segment fault/Bus error occurred at %lx because of %lx (r:%d)\n"
178 "please inform the authors\n",
179 #ifdef __i386__
180 (long)s.eip,
181 #else /* x86_64*/
182 (long)s.rip,
183 #endif
184 (long)s.cr2,siRandomStart);
185 }
186 #ifdef __OPTIMIZE__
187 if(si_restart<3)
188 {
189 si_restart++;
190 fputs("trying to restart...\n",stderr);
191 init_signals();
192 longjmp(si_start_jmpbuf,1);
193 }
194 #endif /* __OPTIMIZE__ */
195 #ifdef CALL_GDB
196 if (sig!=SIGINT)
197 {
198 if (singular_in_batchmode) debug(STACK_TRACE);
199 else debug(INTERACTIVE);
200 }
201 #endif /* CALL_GDB */
202 exit(0);
203 }
204
205 /*---------------------------------------------------------------------*/
206 #elif defined(SunOS) /*SPARC_SUNOS*/
207 /*2
208 * signal handler for run time errors, sparc sunos 4 version
209 */
sigsegv_handler(int sig,int code,struct sigcontext * scp,char * addr)210 void sigsegv_handler(int sig, int code, struct sigcontext *scp, char *addr)
211 {
212 fprintf(stderr,"Singular : signal %d, code %d (v: %d):\n",
213 sig,code,SINGULAR_VERSION);
214 if ((sig!=SIGINT)&&(sig!=SIGABRT))
215 {
216 fprintf(stderr,"current line:>>%s<<\n",my_yylinebuf);
217 fprintf(stderr,"Segment fault/Bus error occurred at %x (r:%d)\n"
218 "please inform the authors\n",
219 (int)addr,siRandomStart);
220 }
221 #ifdef __OPTIMIZE__
222 if(si_restart<3)
223 {
224 si_restart++;
225 fputs("trying to restart...\n",stderr);
226 init_signals();
227 longjmp(si_start_jmpbuf,1);
228 }
229 #endif /* __OPTIMIZE__ */
230 #ifdef CALL_GDB
231 if (sig!=SIGINT) debug(STACK_TRACE);
232 #endif /* CALL_GDB */
233 exit(0);
234 }
235
236 #else
237
238 /*---------------------------------------------------------------------*/
239 /*2
240 * signal handler for run time errors, general version
241 */
sigsegv_handler(int sig)242 void sigsegv_handler(int sig)
243 {
244 fprintf(stderr,"Singular : signal %d (v: %d):\n",
245 sig,SINGULAR_VERSION);
246 if (sig!=SIGINT)
247 {
248 fprintf(stderr,"current line:>>%s<<\n",my_yylinebuf);
249 fprintf(stderr,"Segment fault/Bus error occurred (r:%d)\n"
250 "please inform the authors\n",
251 siRandomStart);
252 }
253 #ifdef __OPTIMIZE__
254 if(si_restart<3)
255 {
256 si_restart++;
257 fputs("trying to restart...\n",stderr);
258 init_signals();
259 longjmp(si_start_jmpbuf,1);
260 }
261 #endif /* __OPTIMIZE__ */
262 #ifdef CALL_GDB
263 if (sig!=SIGINT) debug(STACK_TRACE);
264 #endif /* CALL_GDB */
265 exit(0);
266 }
267 #endif
268
269
270 /*2
271 * signal handler for SIGINT
272 */
273 VAR int sigint_handler_cnt=0;
sigint_handler(int)274 void sigint_handler(int /*sig*/)
275 {
276 mflush();
277 #ifdef HAVE_FEREAD
278 if (fe_is_raw_tty) fe_temp_reset();
279 #endif /* HAVE_FEREAD */
280 char default_opt=' ';
281 if ((feOptSpec[FE_OPT_CNTRLC].value!=NULL)
282 && ((char*)(feOptSpec[FE_OPT_CNTRLC].value))[0])
283 { default_opt=((char*)(feOptSpec[FE_OPT_CNTRLC].value))[0]; }
284 loop
285 {
286 int cnt=0;
287 int c;
288
289 if (singular_in_batchmode)
290 {
291 c = 'q';
292 }
293 else if (default_opt!=' ')
294 {
295 c = default_opt;
296 }
297 else
298 {
299 fprintf(stderr,"// ** Interrupt at cmd:`%s` in line:'%s'\n",
300 Tok2Cmdname(iiOp),my_yylinebuf);
301 if (feOptValue(FE_OPT_EMACS) == NULL)
302 {
303 fputs("abort after this command(a), abort immediately(r), print backtrace(b), continue(c) or quit Singular(q) ?",stderr);
304 fflush(stderr);fflush(stdin);
305 c = fgetc(stdin);
306 }
307 else
308 {
309 c = 'a';
310 }
311 }
312
313 switch(c)
314 {
315 case 'q': case EOF:
316 m2_end(2);
317 case 'r':
318 if (sigint_handler_cnt<3)
319 {
320 sigint_handler_cnt++;
321 fputs("** Warning: Singular should be restarted as soon as possible **\n",stderr);
322 fflush(stderr);
323 extern void my_yy_flush();
324 my_yy_flush();
325 currentVoice=feInitStdin(NULL);
326 longjmp(si_start_jmpbuf,1);
327 }
328 else
329 {
330 fputs("** tried too often, try another possibility **\n",stderr);
331 fflush(stderr);
332 }
333 break;
334 case 'b':
335 VoiceBackTrack();
336 break;
337 case 'a':
338 siCntrlc++;
339 case 'c':
340 if ((feOptValue(FE_OPT_EMACS) == NULL) && (default_opt!=' '))
341 {
342 /* Read until a newline or EOF */
343 while (c != EOF && c != '\n') c = fgetc(stdin);
344 }
345 si_set_signal(SIGINT ,(si_hdl_typ)sigint_handler);
346 return;
347 //siCntrlc ++;
348 //if (siCntrlc>2) si_set_signal(SIGINT,(si_hdl_typ) sigsegv_handler);
349 //else si_set_signal(SIGINT,(si_hdl_typ) sigint_handler);
350 }
351 cnt++;
352 if(cnt>5) m2_end(2);
353 }
354 }
355
356 //void test_int()
357 //{
358 // if (siCntrlc!=0)
359 // {
360 // int saveecho = si_echo;
361 // siCntrlc = FALSE;
362 // si_set_signal(SIGINT ,sigint_handler);
363 // iiDebug();
364 // si_echo = saveecho;
365 // }
366 //}
367
368 # ifndef __OPTIMIZE__
369 VAR volatile int si_stop_stack_trace_x;
370 # ifdef CALL_GDB
debug(int method)371 static void debug (int method)
372 {
373 if (feOptValue(FE_OPT_NO_TTY))
374 {
375 dReportError("Caught Signal 11");
376 return;
377 }
378 /* REMARK FOR NEWER LINUX SYSTEMS:
379 Attaching to a process on Linux with GDB as a normal user may fail with "ptrace:Operation not permitted". By default Linux does not allow attaching to a process which wasn't launched by the debugger (see the Yama security documentation for more details). (https://www.kernel.org/doc/Documentation/security/Yama.txt)
380
381 There are ways to workaround this:
382
383 Run the following command as super user: echo 0| sudo tee /proc/sys/kernel/yama/ptrace_scope
384
385 This will set the ptrace level to 0, after this just with user permissions you can attach to processes which are not launched by the debugger.
386
387 On distributions without Yama (such as Raspbian) you can use libcap2-bin to assign ptrace permissions to specific executables: sudo setcap cap_sys_ptrace=eip /usr/bin/gdb
388 */
389 int pid;
390 char buf[16];
391 char * args[4] = { (char*)"gdb", (char*)"Singular", NULL, NULL };
392
393 #ifdef HAVE_FEREAD
394 if (fe_is_raw_tty) fe_temp_reset();
395 #endif /* HAVE_FEREAD */
396
397 sprintf (buf, "%d", getpid ());
398
399 args[2] = buf;
400
401 pid = fork ();
402 if (pid == 0)
403 {
404 switch (method)
405 {
406 case INTERACTIVE:
407 fputs ("\n\nquit with \"p si_stop_stack_trace_x=0\"\n\n\n",stderr);
408 debug_stop (args);
409 break;
410 case STACK_TRACE:
411 fputs ("stack_trace\n",stderr);
412 stack_trace (args);
413 break;
414 default:
415 // should not be reached:
416 exit(1);
417 }
418 }
419 else if (pid == -1)
420 {
421 perror ("could not fork");
422 return;
423 }
424
425 si_stop_stack_trace_x = 1;
426 while (si_stop_stack_trace_x) ;
427 }
428
debug_stop(char * const * args)429 static void debug_stop (char *const*args)
430 {
431 execvp (args[0], args);
432 perror ("exec failed");
433 _exit (0);
434 }
435 # endif /* CALL_GDB */
436
stack_trace(char * const * args)437 static void stack_trace (char *const*args)
438 {
439 int pid;
440 int in_fd[2];
441 int out_fd[2];
442 fd_set fdset;
443 fd_set readset;
444 struct timeval tv;
445 int sel, index, state;
446 char buffer[256];
447 char c;
448
449 if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
450 {
451 perror ("could open pipe");
452 m2_end(999);
453 }
454
455 pid = fork ();
456 if (pid == 0)
457 {
458 si_close (0); si_dup2 (in_fd[0],0); /* set the stdin to the in pipe */
459 si_close (1); si_dup2 (out_fd[1],1); /* set the stdout to the out pipe */
460 si_close (2); si_dup2 (out_fd[1],2); /* set the stderr to the out pipe */
461
462 execvp (args[0], args); /* exec gdb */
463 perror ("exec failed");
464 m2_end(999);
465 }
466 else if (pid == -1)
467 {
468 perror ("could not fork");
469 m2_end(999);
470 }
471
472 FD_ZERO (&fdset);
473 FD_SET (out_fd[0], &fdset);
474
475 si_write (in_fd[1], "backtrace\n", 10);
476 si_write (in_fd[1], "p si_stop_stack_trace_x = 0\n", 28);
477 si_write (in_fd[1], "quit\n", 5);
478
479 index = 0;
480 state = 0;
481
482 loop
483 {
484 readset = fdset;
485 tv.tv_sec = 1;
486 tv.tv_usec = 0;
487
488 sel = si_select (FD_SETSIZE, &readset, NULL, NULL, &tv);
489 if (sel == -1)
490 break;
491
492 if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
493 {
494 if (si_read (out_fd[0], &c, 1))
495 {
496 switch (state)
497 {
498 case 0:
499 if (c == '#')
500 {
501 state = 1;
502 index = 0;
503 buffer[index++] = c;
504 }
505 break;
506 case 1:
507 buffer[index++] = c;
508 if ((c == '\n') || (c == '\r'))
509 {
510 buffer[index] = 0;
511 fputs (buffer,stderr);
512 state = 0;
513 index = 0;
514 }
515 break;
516 default:
517 break;
518 }
519 }
520 }
521 else if (si_stop_stack_trace_x==0)
522 break;
523 }
524
525 si_close (in_fd[0]);
526 si_close (in_fd[1]);
527 si_close (out_fd[0]);
528 si_close (out_fd[1]);
529 m2_end(0);
530 }
531
532 # endif /* !__OPTIMIZE__ */
533
534 /// init signal handlers and error handling for libraries: NTL, factory
init_signals()535 void init_signals()
536 {
537 // NTL error handling (>= 9.3.0) ----------------------------------------
538 #ifdef HAVE_NTL
539 #if (((NTL_MAJOR_VERSION==9)&&(NTL_MINOR_VERSION>=3))||(NTL_MAJOR_VERSION>=10))
540 ErrorMsgCallback=WerrorS;
541 ErrorCallback=HALT;
542 #endif
543 #endif
544
545 // signal handler -------------------------------------------------------
546 #ifdef SIGSEGV
547 si_set_signal(SIGSEGV,(si_hdl_typ)sigsegv_handler);
548 #endif
549 #ifdef SIGBUS
550 si_set_signal(SIGBUS, (si_hdl_typ)sigsegv_handler);
551 #endif
552 #ifdef SIGFPE
553 si_set_signal(SIGFPE, (si_hdl_typ)sigsegv_handler);
554 #endif
555 #ifdef SIGILL
556 si_set_signal(SIGILL, (si_hdl_typ)sigsegv_handler);
557 #endif
558 #ifdef SIGIOT
559 si_set_signal(SIGIOT, (si_hdl_typ)sigsegv_handler);
560 #endif
561 si_set_signal(SIGINT ,(si_hdl_typ)sigint_handler);
562 si_set_signal(SIGCHLD, (si_hdl_typ)sig_chld_hdl);
563 si_set_signal(SIGPIPE, (si_hdl_typ)sig_pipe_hdl);
564 si_set_signal(SIGTERM, (si_hdl_typ)sig_term_hdl);
565 }
566
567