source: git/Singular/cntrlc.cc

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