Set the cut path properly when a non-default output path is specified
[atscap.git] / xtc_signals.c
blob1990f971b9c55721310f661bacbc4fa4227fc156
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 *****************************************************************************/
21 #ifdef USE_CONSOLE
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()
33 /* ESC [ A */
34 #define KR_UP 0x1B5B41
35 #define KB_UP 0xF1
36 /* ESC [ B */
37 #define KR_DN 0x1B5B42
38 #define KB_DN 0xF2
39 /* ESC [ C */
40 #define KR_RT 0x1B5B43
41 #define KB_RT 0xF3
42 /* ESC [ D */
43 #define KR_LF 0x1B5B44
44 #define KB_LF 0xF4
46 /* things get a bit odder here. may have to redefine if not xterm/aterm. */
47 /* ESC [ 1 ~ */
48 #define KR_HM 0x1B5B317E
49 #define KB_HM 0xF5
50 /* ESC [ 2 ~ */
51 #define KR_IN 0x1B5B327E
52 #define KB_IN 0xF6
53 /* ESC [ 3 ~ */
54 #define KR_DL 0x1B5B337E
55 #define KB_DL 0xF7
56 /* ESC [ 4 ~ */
57 #define KR_EN 0x1B5B347E
58 #define KB_EN 0xF8
59 /* ESC [ 5 ~ */
60 #define KR_PU 0x1B5B357E
61 #define KB_PU 0xF9
62 /* ESC [ 6 ~ */
63 #define KR_PD 0x1B5B367E
64 #define KB_PD 0xFA
65 #include <termios.h>
66 int console_init = 0;
68 #endif /* KR_UP */
70 /* will need for signal actions, sigterm mostly */
71 #ifdef USE_SIGNALS
72 #warning using POSIX signals
73 #include <signal.h>
75 volatile int sig_val = 0;
76 int sig_kill = 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"
83 /* prototype */
84 void signal_test( void );
85 #endif
88 #ifdef USE_CONSOLE
89 /* put the console back to a usable state */
90 /* static */
91 void
92 console_reset ( void )
94 struct termios modes;
95 int f;
97 f = 0;
98 fcntl( 0, F_GETFL, &f );
99 f &= ~O_NONBLOCK;
100 fcntl( 0, F_SETFL, f );
102 /* indicate the console will need init before nonblock nonecho use */
103 console_init = 1;
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" );
113 /* static */
114 void console_exit ( int err )
116 console_reset();
117 console_reset();
118 c_exit(err);
121 /* non-blocking non-echoing single-char stdin, from mzplay.c */
122 /* static */
124 console_getch ( void ) {
125 unsigned c = 0;
127 #ifdef USE_SIGNALS
128 #warning console_getch checks sig_kill
129 signal_test(); /* any flags worth noticing? */
130 if (0 != sig_kill) console_exit(0);
131 #endif
133 if (console_init) {
134 struct termios modes;
135 int f;
137 console_init = 0;
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" );
148 f = 0;
149 fcntl( 0, F_GETFL, &f );
150 f |= O_NONBLOCK;
151 fcntl( 0, F_SETFL, f );
153 if ( read( 0, &c, 1 ) < 1 ) c = 0;
154 return c;
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
160 /* static */
161 void
162 console_scan_esc ( unsigned char *kb )
164 unsigned char k;
165 int arrow;
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); */
180 arrow = 0;
181 /* "ESC [" and 4 choices for arrows are A B C D */
182 switch( *kb ) {
183 /* no chars to clear after these */
184 case 'A':
185 *kb = KB_UP; /* UP ARROW */
186 arrow = ~0;
187 break;
189 case 'B':
190 *kb = KB_DN; /* DOWN ARROW */
191 arrow = ~0;
192 break;
194 case 'C':
195 *kb = KB_RT; /* RIGHT ARROW */
196 arrow = ~0;
197 break;
199 case 'D':
200 *kb = KB_LF;
201 arrow = ~0;
202 break;
204 case 'H':
205 *kb = KB_HM; /* HOME */
206 arrow = ~0;
207 break;
209 case 'F':
210 *kb = KB_EN; /* END */
211 arrow = ~0;
212 break;
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 */
220 k = console_getch();
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 */
225 switch ( *kb ) {
227 case '7': /* newer aterm doing this? */
228 case '1':
229 *kb = KB_HM; /* HOME */
230 break;
232 case '2':
233 *kb = KB_IN; /* INSERT */
234 break;
236 case '3':
237 *kb = KB_DL; /* DELETE */
238 break;
240 case '8': /* newer aterm doing this? */
241 case '4':
242 *kb = KB_EN; /* END */
243 break;
245 case '5':
246 *kb = KB_PU; /* PAGE UP */
247 break;
248 case '6':
249 *kb = KB_PD; /* PAGE DOWN */
250 break;
251 default:
252 *kb = 0; /* try to prevent false triggers if no conditions met */
253 break;
256 return;
259 /* wrapper to return keyboard special keys as single byte */
260 /* static */
261 void
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 */
271 #ifdef USE_SIGNALS
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
277 /* static */
278 void
279 dump_backtrace ( char **bts, size_t z, void *addr )
281 FILE *f;
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 */
287 char *p, *r;
288 size_t y;
289 unsigned int i;
290 char has_name, has_addr, has_func;
292 if (0 == z) return;
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);
303 f = fopen( o, "wb");
305 /* make .sh executable to call addr2line.sh to help debug */
306 if (NULL != f) chmod( o, 0755 );
308 if (NULL != f) {
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);
317 fprintf( f, "#\n");
319 /* start at 1, because 0 is always signal_handler */
320 for (y = 1; y < z; y++) {
321 i = 0;
323 /* zero strings */
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;
334 /* has name ? */
335 if (0 != has_name) {
336 astrncpy( n, bts[ y ], sizeof(n));
337 if (NULL != strstr( n, NAME )) {
338 strcpy( 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;
354 /* has address? */
355 if (0 != has_addr) {
356 p = strchr( t, '[' ); /* point to addr */
357 if (NULL != p) {
358 p++;
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;
371 *s = 0;
373 /* has function? */
374 if (0 != has_func) {
375 p = strchr( t, '(' );
376 if (NULL != p) {
377 p++;
378 r = strchr( p, ')' );
379 if (NULL != r) {
380 *r = 0;
381 astrncpy( s, p, sizeof(s) );
386 /* atscap default install location */
387 #define USE_PREFIX_BIN "/usr/local/bin/"
389 /* if file open */
390 if (NULL != f) {
391 fprintf( f, "# %s\n", bts[ y ] );
392 if (0 != *n) {
393 if ( '/' != *n)
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);
407 fflush( stdout );
409 nanosleep( &console_read_sleep, NULL );
410 console_reset();
411 console_reset();
412 system( o );
413 free( bts );
414 exit( 252 );
415 fprintf( stdout, "\n");
417 #endif
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.
423 /* static */
424 void
425 /* signal_handler ( int sigval ) */ /* old style */
426 signal_handler ( int sigval, siginfo_t *info, void *secret )
428 #ifdef USE_GNU_BACKTRACE
429 void *bt[BTZ];
430 size_t z;
431 char t[256];
432 int f;
433 #ifdef USE_GNU_BACKTRACE_SCRIPT
434 char **bts = (char **) NULL;
435 #endif
436 #endif
437 ucontext_t *uc;
438 void *addr;
439 char n[256];
441 // return;
443 *n = 0;
445 addr = NULL;
447 /* EIP only works with GNU/Linux on intel/amd x86 32-bit arch */
448 #if defined(REG_EIP)
449 #warning using x86 arch uc_mcontext.gregs REG_EIP
450 uc = (ucontext_t *)secret;
451 addr = (void *) uc->uc_mcontext.gregs[ REG_EIP ];
452 #endif
453 /* RIP only works with GNU/Linux on intel/amd x86_64 64-bit arch */
454 #if defined(REG_RIP)
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 ];
458 #endif
460 sig_val = sigval; /* save signal for exit processing */
462 switch (sig_val)
465 /* ignore sigpipe, is likely to be web server aborted connection */
466 case SIGPIPE:
467 return;
468 break;
470 /* only sets flag for signal test called from console scan */
471 case SIGWINCH:
472 return;
473 break;
474 case SIGTERM:
475 return;
476 break;
477 case SIGQUIT:
478 return;
479 break;
480 case SIGINT:
481 return;
482 break;
484 #ifdef USE_GNU_BACKTRACE
485 case SIGUSR1:
486 z = backtrace( bt, BTZ);
487 if (0 == z) break;
489 /* addr will be arch dependent on x86 and derivatives with newer GCC only */
490 bt[2] = addr;
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 */
500 date_now );
502 f = open( n, O_RDWR | O_CREAT, 0644 );
504 if (f > 2) {
505 write( f, t, strlen(t) );
507 /* use btfd.sh to extract line numbers */
508 backtrace_symbols_fd( bt, z, f );
509 fsync( f );
510 close( f );
512 return;
513 break;
514 #endif
516 /* fatal errors */
517 case SIGSEGV:
518 case SIGILL:
519 case SIGFPE:
520 case SIGBUS:
521 case SIGIOT:
523 #ifdef USE_GNU_BACKTRACE
524 /* if backtrace or backtrace_symbols fail, give some exit indication */
525 z = backtrace( bt, BTZ);
526 if (0 == z) {
527 fprintf( stderr, CLS BN SCV
528 "Fatal error at addr %p, no backtrace.\n", addr );
529 console_reset();
530 console_reset();
531 exit(254);
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.
537 bt[2] = addr;
539 #ifdef USE_GNU_BACKTRACE_SCRIPT
540 /* Save backtrace to addr2line script. Is problematic if stack is crashed. */
541 bts = backtrace_symbols( bt, z );
542 if (NULL == bts) {
543 fprintf( stderr, CLS BN SCV
544 "Fatal error at addr %p, no backtrace.\n", addr );
545 console_reset();
546 console_reset();
547 exit(255);
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 );
553 #if 0
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 );
557 #endif
559 dump_backtrace( bts, z, addr ); /* does not return, exit252 */
560 #else
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 */
571 date_now );
573 f = open( n, O_RDWR | O_CREAT | O_TRUNC, 0644 );
574 if (f > 2) {
575 write( f, t, strlen(t) );
576 backtrace_symbols_fd( bt, z, f );
577 fsync(f);
578 close(f);
581 /* USE_GNU_BACKTRACE_SCRIPT */
582 #endif
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);
592 #endif
593 fprintf(stderr, "SIG%s at addr %p\n", sig_text[sig_val], addr);
595 console_reset();
596 console_reset();
597 exit(253); // 0xFC
598 break;
600 /* the rest get logged */
601 default:
602 if (sig_val < 32) {
603 fprintf( stdout, "%s %s(%d) tid %d ignored",
604 WHO, sig_text[ sig_val ], sigval, getpid() );
605 } else {
606 fprintf( stdout, "%s %d", WHO, sig_val);
608 break;
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 */
617 /* static */
618 void
619 signal_init ( void )
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 */
661 /* static */
662 void
663 sigint_set ( int s )
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 );
678 /* USE_SIGNALS */
679 #endif
683 #ifdef USE_SIGNALS
684 /* want to close the device properly if possible */
685 /* static */
686 void
687 signal_test ( void )
689 if (0 == sig_val) return; /* nothing to do? */
690 if (sig_val < 32)
691 fprintf( stderr, "received SIG%s", sig_text[sig_val] );
693 switch( sig_val )
696 /* these three indicate user requested program terminate */
697 case SIGINT:
698 case SIGTERM:
699 case SIGQUIT:
700 sig_kill = ~0;
701 fprintf( stderr, "exiting on signal %d", sig_val);
702 break;
704 /* GNU screen changed the term size, or xterm was resized */
705 case SIGWINCH:
706 fprintf( stderr, "%s tty dimension changed", WHO);
707 break;
709 case SIGPIPE:
710 fprintf( stderr, "%s broken pipe or socket", WHO);
711 break;
713 /* even with SEGV still want to try to close device properly if possible */
714 case SIGSEGV:
715 sig_kill = ~0;
716 break;
718 default:
719 if (sig_val < 32)
720 fprintf( stderr, "%s signal %s ignored",
721 WHO, sig_text[sig_val] );
722 break;
724 sig_val = 0;
727 #endif
730 #if 0
731 #ifdef USE_SIGNALS
732 /* disable control c */
733 sigint_set( 0 );
734 #endif
736 #ifdef USE_SIGNALS
737 /* enable control c */
738 sigint_set( 1 );
739 #endif
741 #ifdef USE_SIGNALS
742 /* initialize signal handler */
743 signal_init(); /* sigaction setup for control c trap */
744 #endif
745 signal_timeout = 0;
746 #endif