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