1 /*****************************************************************************
3 * signals.c (c) Copyright 2004-2007 by inkling@nop.org
4 * part of the ATSC Transport Stream Capture Application Programs
6 * atscap is free software; you may only redistribute it and/or modify
7 * it under the terms of the GNU General Public License Version 2, or later,
8 * as published by the Free Software Foundation.
10 * atscap source code is distributed to you in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY OR SUPPORT; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Please see the
13 * GNU General Public License Version 3 for more details.
15 * You should have received a copy of the GNU General Public License Version 2
16 * along with this program; if not, write me or the Free Software Foundation,
17 * Inc., at 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *****************************************************************************/
22 #warning using console_* functions
23 /* console scan esc uses
24 kb remap of special esc keys to single byte values
25 move these around to anything above 127 to suit yourself
26 vt102 1980 standard stuff? maybe
28 KR_ is keyboard raw return byte string from read(0,...)
30 KB_ is keyboard cooked to something simpler for console_scan()
34 #define KR_UP 0x1B5B41
37 #define KR_DN 0x1B5B42
40 #define KR_RT 0x1B5B43
43 #define KR_LF 0x1B5B44
46 /* things get a bit odder here. may have to redefine if not xterm/aterm. */
48 #define KR_HM 0x1B5B317E
51 #define KR_IN 0x1B5B327E
54 #define KR_DL 0x1B5B337E
57 #define KR_EN 0x1B5B347E
60 #define KR_PU 0x1B5B357E
63 #define KR_PD 0x1B5B367E
70 /* will need for signal actions, sigterm mostly */
72 #warning using POSIX signals
75 volatile int sig_val
= 0;
77 char *sig_text
[32] = {
78 "NULL", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "BUS",
79 "FPE", "KILL", "USR1", "SEGV", "USR2", "PIPE", "ALRM", "TERM",
80 "STKFLT", "CHLD", "CONT", "STOP", "TSTP", "TTIN", "TTOU", "URG",
81 "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "IO", "PWR", "SYS"
84 void signal_test( void );
89 /* put the console back to a usable state */
92 console_reset ( void )
98 fcntl( 0, F_GETFL
, &f
);
100 fcntl( 0, F_SETFL
, f
);
102 /* indicate the console will need init before nonblock nonecho use */
104 if ( tcgetattr( 0, &modes
) < 0 )
105 fprintf( stderr
, "c_reset tcgetattr" );
107 modes
.c_lflag
|= ICANON
;
108 modes
.c_lflag
|= ECHO
;
109 if ( tcsetattr( 0, TCSAFLUSH
, &modes
) < 0 )
110 fprintf( stderr
, "c_reset tcsetattr" );
114 void console_exit ( int err
)
121 /* non-blocking non-echoing single-char stdin, from mzplay.c */
124 console_getch ( void ) {
128 #warning console_getch checks sig_kill
129 signal_test(); /* any flags worth noticing? */
130 if (0 != sig_kill
) console_exit(0);
134 struct termios modes
;
139 if ( tcgetattr(0, &modes
) < 0 )
140 fprintf( stderr
, "c_getch tcgetattr" );
142 modes
.c_lflag
&= ~ICANON
;
143 modes
.c_lflag
&= ~ECHO
;
145 if ( tcsetattr(0, TCSAFLUSH
, &modes
) < 0 )
146 fprintf( stderr
, "c_getch tcsetattr" );
149 fcntl( 0, F_GETFL
, &f
);
151 fcntl( 0, F_SETFL
, f
);
153 if ( read( 0, &c
, 1 ) < 1 ) c
= 0;
157 /* when console scan gets ESC, look for [ and if so do these
158 make easier to use with following keys: up/dn/lt/rt/in/dl/hm/en/pu/pd
162 console_scan_esc ( unsigned char *kb
)
167 *kb
= console_getch(); /* get char after ESC */
168 if (0 == *kb
) return; /* nothing pending from function keys */
169 if ('[' != *kb
) return; /* no [ means no arrows or function keys */
172 /* utstpg = utsnow + PROGRAM_GUIDE_TIMEOUT; */
174 /* nanosleep( &console_read_sleep, NULL); */
175 *kb
= console_getch(); /* get what follows ESC [ */
177 /* should be cursor/arrow keys */
178 /* fprintf( stdout, "ESC[ %02X", *kb); */
181 /* "ESC [" and 4 choices for arrows are A B C D */
183 /* no chars to clear after these */
185 *kb
= KB_UP
; /* UP ARROW */
190 *kb
= KB_DN
; /* DOWN ARROW */
195 *kb
= KB_RT
; /* RIGHT ARROW */
205 *kb
= KB_HM
; /* HOME */
210 *kb
= KB_EN
; /* END */
216 /* have an arrow so get out, no need for ~ term */
217 if (arrow
!= 0) return;
219 /* checked for arrows, kb still has numeric, so check for ~ term char */
221 if (0 == k
) { *kb
= 0; return; } /* it's incomplete so abort */
222 if ('~' != k
) { *kb
= 0; return; } /* no ~ term char so abort */
224 /* check char after ESC [ again */
227 case '7': /* newer aterm doing this? */
229 *kb
= KB_HM
; /* HOME */
233 *kb
= KB_IN
; /* INSERT */
237 *kb
= KB_DL
; /* DELETE */
240 case '8': /* newer aterm doing this? */
242 *kb
= KB_EN
; /* END */
246 *kb
= KB_PU
; /* PAGE UP */
249 *kb
= KB_PD
; /* PAGE DOWN */
252 *kb
= 0; /* try to prevent false triggers if no conditions met */
259 /* wrapper to return keyboard special keys as single byte */
262 console_getch_fn ( unsigned char *kb
)
264 *kb
= console_getch();
265 if (0x1B != *kb
) return; /* let normal keys slide */
266 console_scan_esc( kb
); /* try to parse function keys */
268 #endif /* USE_CONSOLE */
272 /* Dump backtrace to file and to stderr after clearing screen */
273 /* Check ALL pointers before output in case it's toast. */
274 /* NOTE: It should be pointed out that this is a Bad Idea. */
275 #ifdef USE_GNU_BACKTRACE_SCRIPT
276 #warning using GNU backtrace() script
279 dump_backtrace ( char **bts
, size_t z
, void *addr
)
282 char a
[128+3]; /* address, up to 64 bits, plus 3 for 0x and nul */
283 char o
[256]; /* dump output name */
284 char t
[256]; /* temp copy of backtrace string */
285 char n
[256]; /* app or lib name */
286 char s
[256]; /* source function after address + name extract */
290 char has_name
, has_addr
, has_func
;
293 if (NULL
== bts
) return;
295 has_name
= has_addr
= has_func
= 0;
297 /* Crash log script is /dtv/atscap#-pid.sh, calling addr2line for line numbers.
298 NOTE: This only works if crash log matches current version compiled.
300 snprintf( o
, sizeof(o
), "%s%s-%d.sh",
301 out_path
, NAME
, (int)pid_m
);
305 /* make .sh executable to call addr2line.sh to help debug */
306 if (NULL
!= f
) chmod( o
, 0755 );
309 fprintf( f
, "# %s\n", o
);
310 fprintf( f
, "echo %s%d-%s SIG%s at address %p on %s.\n",
311 NAME
, arg_devnum
, VERSION
,
312 sig_text
[sig_val
], addr
, date_now
);
313 fprintf( f
, "# addr2line helps find backtrace() line number.\n");
314 fprintf( f
, "# Comments below are strings from backtrace().\n");
315 fprintf( f
, "# Only %s lines are used for line numbers.\n", NAME
);
316 fprintf( f
, "# Found %d stack frames.\n", z
);
319 /* start at 1, because 0 is always signal_handler */
320 for (y
= 1; y
< z
; y
++) {
324 memset( t
, 0, sizeof(t
) );
325 memset( s
, 0, sizeof(s
) );
326 memset( n
, 0, sizeof(n
) );
327 memset( a
, 0, sizeof(a
) );
329 /* copy backtrace string for editing, first time */
330 astrncpy( t
, bts
[ y
], sizeof(t
) );
332 if ( '[' != *t
) has_name
= ~0;
336 astrncpy( n
, bts
[ y
], sizeof(n
));
337 if (NULL
!= strstr( n
, NAME
)) {
340 /* remove anything after name, work backwards from [ ( and blank */
341 p
= strchr( n
, '[' ); /* remove addr */
342 if (NULL
!= p
) *p
= 0;
343 p
= strchr( n
, '(' ); /* remove func */
344 if (NULL
!= p
) *p
= 0;
345 p
= strchr( n
, ' ' ); /* remove blank */
346 if (NULL
!= p
) *p
= 0;
349 /* copy backtrace string for editing, again */
350 astrncpy( t
, bts
[ y
], sizeof(t
) );
351 p
= strchr( t
, '[' );
352 if (NULL
!= p
) has_addr
= ~0;
356 p
= strchr( t
, '[' ); /* point to addr */
359 sscanf( p
, "%x", &i
); /* get addr */
361 /* convert all hexadecimal displays to upper case */
362 snprintf( a
, sizeof(a
), "0x%08X", i
);
366 /* copy backtrace string for editing, again */
367 astrncpy( t
, bts
[ y
], sizeof(t
) );
368 p
= strchr( t
, '(' );
369 if (NULL
!= p
) has_func
= ~0;
375 p
= strchr( t
, '(' );
378 r
= strchr( p
, ')' );
381 astrncpy( s
, p
, sizeof(s
) );
386 /* atscap default install location */
387 #define USE_PREFIX_BIN "/usr/local/bin/"
391 fprintf( f
, "# %s\n", bts
[ y
] );
394 fprintf( f
, "addr2line -s -f -e %s%s %s # %s\n",
395 USE_PREFIX_BIN
, n
, a
, s
);
400 if (NULL
!= f
) fprintf( f
, "# EOF\n");
403 if (NULL
!= f
) fclose(f
);
405 /* let user know it's all bad */
406 fprintf( stdout
, "Running %s to get backtrace lines:\n\n", o
);
409 nanosleep( &console_read_sleep
, NULL
);
415 fprintf( stdout
, "\n");
419 /* See http://www.linuxjournal.com/article/6391 Listing 3. */
420 /* NOTE: stack crashes will prevent most of this from working right,
421 but simpler errors like NULL pointers may be more easily found.
425 /* signal_handler ( int sigval ) */ /* old style */
426 signal_handler ( int sigval
, siginfo_t
*info
, void *secret
)
428 #ifdef USE_GNU_BACKTRACE
433 #ifdef USE_GNU_BACKTRACE_SCRIPT
434 char **bts
= (char **) NULL
;
447 /* EIP only works with GNU/Linux on intel/amd x86 32-bit arch */
449 #warning using x86 arch uc_mcontext.gregs REG_EIP
450 uc
= (ucontext_t
*)secret
;
451 addr
= (void *) uc
->uc_mcontext
.gregs
[ REG_EIP
];
453 /* RIP only works with GNU/Linux on intel/amd x86_64 64-bit arch */
455 #warning using x86_64 arch uc_mcontext.gregs REG_RIP
456 uc
= (ucontext_t
*)secret
;
457 addr
= (void *) uc
->uc_mcontext
.gregs
[ REG_RIP
];
460 sig_val
= sigval
; /* save signal for exit processing */
465 /* ignore sigpipe, is likely to be web server aborted connection */
470 /* only sets flag for signal test called from console scan */
484 #ifdef USE_GNU_BACKTRACE
486 z
= backtrace( bt
, BTZ
);
489 /* addr will be arch dependent on x86 and derivatives with newer GCC only */
492 snprintf( n
, sizeof(n
), "%s%s%d-%d.bt",
493 out_path
, NAME
, arg_devnum
, pid_m
);
494 snprintf( t
, sizeof(t
),
495 "# %s%d-%s SIG%s at addr %p pid %d tid %d (%d) on %s\n\n",
496 NAME
, arg_devnum
, VERSION
,
497 sig_text
[sig_val
], addr
, pid_m
,
498 (int) pthread_self(),
499 0x3FFF & (int) pthread_self(), /* ignore if NPTL */
502 f
= open( n
, O_RDWR
| O_CREAT
, 0644 );
505 write( f
, t
, strlen(t
) );
507 /* use btfd.sh to extract line numbers */
508 backtrace_symbols_fd( bt
, z
, f
);
523 #ifdef USE_GNU_BACKTRACE
524 /* if backtrace or backtrace_symbols fail, give some exit indication */
525 z
= backtrace( bt
, BTZ
);
527 fprintf( stderr
, CLS BN SCV
528 "Fatal error at addr %p, no backtrace.\n", addr
);
534 /* This is typical place where stack trace goes wrong, for pthreads. All
535 examples of how to use have bt[1] instead, but are not pthread apps.
539 #ifdef USE_GNU_BACKTRACE_SCRIPT
540 /* Save backtrace to addr2line script. Is problematic if stack is crashed. */
541 bts
= backtrace_symbols( bt
, z
);
543 fprintf( stderr
, CLS BN SCV
544 "Fatal error at addr %p, no backtrace.\n", addr
);
550 /* If you're here, it must have crashed. LET ME KNOW or send me a patch. */
551 /* It logs what can be logged about crash. addr2line will help, somewhat. */
552 fprintf( stderr
, CLS SCV BN
);
554 fprintf( stderr
, "%s%d-%s SIG%s at addr %p on %s.\n",
555 NAME
, arg_devnum
, VERSION
,
556 sig_text
[sig_val
], addr
, date_now
);
559 dump_backtrace( bts
, z
, addr
); /* does not return, exit252 */
561 /* Save backtrace to file instead. Is problematic if stack is crashed. */
562 /* snprintf( n, sizeof(n), "%s%s-%d.bt", out_path, NAME, pid_m ); */
563 snprintf( n
, sizeof(n
), "%s%s%d-%d.bt",
564 out_path
, NAME
, arg_devnum
, getpid());
565 snprintf( t
, sizeof(t
),
566 "# %s%d-%s SIG%s pid %d tid %d (%d) on %s\n",
567 NAME
, arg_devnum
, VERSION
,
568 sig_text
[sig_val
], pid_m
,
569 (int) pthread_self(),
570 0x3FFF & (int) pthread_self(), /* ignore if NPTL */
573 f
= open( n
, O_RDWR
| O_CREAT
| O_TRUNC
, 0644 );
575 write( f
, t
, strlen(t
) );
576 backtrace_symbols_fd( bt
, z
, f
);
581 /* USE_GNU_BACKTRACE_SCRIPT */
583 /* USE_GNU_BACKTRACE */
585 /* NOTE: if stack is crashed, these won't help and may make it worse */
586 fprintf( stderr
, CLS SCV BN
587 "%s%d-%s SIG%s at addr %p on %s.\n",
588 NAME
, arg_devnum
, VERSION
,
589 sig_text
[sig_val
], addr
, date_now
);
591 fprintf(stderr
, "Run btfd.sh <%s for stack trace.\n\n", n
);
593 fprintf(stderr
, "SIG%s at addr %p\n", sig_text
[sig_val
], addr
);
600 /* the rest get logged */
603 fprintf( stdout
, "%s %s(%d) tid %d ignored",
604 WHO
, sig_text
[ sig_val
], sigval
, getpid() );
606 fprintf( stdout
, "%s %d", WHO
, sig_val
);
611 fprintf( stderr
, "%s-%s unhandled SIG%s at addr %p\n\n",
612 NAME
, VERSION
, sig_text
[sig_val
], addr
);
613 console_exit( sig_val
);
616 /* global signal init. got rid of -ansi -pedantic porting errors */
621 struct sigaction act
;
623 /* act.sa_handler = signal_handler; */
624 /* old style doesn't give EIP */
626 /* sigemptyset is required */
627 sigemptyset( &act
.sa_mask
);
628 act
.sa_flags
= SA_RESTART
| SA_SIGINFO
;
629 act
.sa_sigaction
= signal_handler
;
631 /* signals quit and term can be used to terminate it gracefully */
632 sigaction( SIGQUIT
, &act
, NULL
);
633 sigaction( SIGTERM
, &act
, NULL
);
635 /* control c will terminate it gracefully if not capturing */
636 sigaction( SIGINT
, &act
, NULL
);
638 /* act.sa_handler = SIG_IGN; */
640 /* console resize, using something else */
641 sigaction( SIGWINCH
, &act
, NULL
);
643 /* HTTP remote closed connection is usual cause of this.
644 Example: refresh the page before it's done loading.
646 sigaction( SIGPIPE
, &act
, NULL
);
648 /* these are all fatal */
649 sigaction( SIGSEGV
, &act
, NULL
);
650 sigaction( SIGILL
, &act
, NULL
);
651 sigaction( SIGFPE
, &act
, NULL
);
652 sigaction( SIGBUS
, &act
, NULL
);
653 sigaction( SIGIOT
, &act
, NULL
);
655 /* SIGUSR1 will dump a backtrace log of current threads */
656 sigaction( SIGUSR1
, &act
, NULL
);
660 /* SIGINT enable 1, disable 0 */
665 struct sigaction act
;
667 /* sigemptyset is required */
668 sigemptyset( &act
.sa_mask
);
669 act
.sa_flags
= SA_RESTART
;
670 act
.sa_handler
= SIG_IGN
;
672 /* if (0 != s) act.sa_handler = signal_handler; */ /* old style */
673 if (0 != s
) act
.sa_sigaction
= signal_handler
;
675 sigaction( SIGINT
, &act
, NULL
);
684 /* want to close the device properly if possible */
689 if (0 == sig_val
) return; /* nothing to do? */
691 fprintf( stderr
, "received SIG%s", sig_text
[sig_val
] );
696 /* these three indicate user requested program terminate */
701 fprintf( stderr
, "exiting on signal %d", sig_val
);
704 /* GNU screen changed the term size, or xterm was resized */
706 fprintf( stderr
, "%s tty dimension changed", WHO
);
710 fprintf( stderr
, "%s broken pipe or socket", WHO
);
713 /* even with SEGV still want to try to close device properly if possible */
720 fprintf( stderr
, "%s signal %s ignored",
721 WHO
, sig_text
[sig_val
] );
732 /* disable control c */
737 /* enable control c */
742 /* initialize signal handler */
743 signal_init(); /* sigaction setup for control c trap */