add grabshot, move snapshot, asset update mtime, plugins.txt for ffmpeg 3.4.1
[goodguy/history.git] / cinelerra-5.1 / guicast / bcsignals.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcsignals.h"
23 #include "bcwindowbase.h"
24 #include "bccmodels.h"
25 #include "bckeyboard.h"
26 #include "bcresources.h"
27 #include "cstrdup.h"
28
29 #include <ctype.h>
30 #include <dirent.h>
31 #include <execinfo.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <sys/ioctl.h>
40 #include <sys/prctl.h>
41 #include <sys/types.h>
42
43 BC_Signals* BC_Signals::global_signals = 0;
44 static int signal_done = 0;
45
46 static struct sigaction old_segv = {0, }, old_intr = {0, };
47 static void handle_dump(int n, siginfo_t * info, void *sc);
48
49 const char *BC_Signals::trap_path = 0;
50 void *BC_Signals::trap_data = 0;
51 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
52 bool BC_Signals::trap_sigsegv = false;
53 bool BC_Signals::trap_sigintr = false;
54
55 static void uncatch_sig(int sig, struct sigaction &old)
56 {
57         struct sigaction act;
58         sigaction(sig, &old, &act);
59         old.sa_handler = 0;
60 }
61
62 static void catch_sig(int sig, struct sigaction &old)
63 {
64         struct sigaction act;
65         memset(&act, 0, sizeof(act));
66         act.sa_sigaction = handle_dump;
67         act.sa_flags = SA_SIGINFO;
68         sigaction(sig, &act, (!old.sa_handler ? &old : 0));
69 }
70
71 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
72 static void catch_intr() { catch_sig(SIGINT, old_intr); }
73 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
74 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
75
76 void BC_Signals::set_trap_path(const char *path)
77 {
78         trap_path = path;
79 }
80
81 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
82 {
83         trap_data = data;
84         trap_hook = hook;
85 }
86
87 void BC_Signals::set_catch_segv(bool v) {
88         if( v == trap_sigsegv ) return;
89         if( v ) catch_segv();
90         else uncatch_segv();
91         v = trap_sigsegv;
92 }
93
94 void BC_Signals::set_catch_intr(bool v) {
95         if( v == trap_sigintr ) return;
96         if( v ) catch_intr();
97         else uncatch_intr();
98         v = trap_sigintr;
99 }
100
101 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
102 {
103         va_list ap;    va_start(ap, fmt);
104         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
105         va_end(ap);
106         FILE *ifp = fopen(bfr,"r");
107         if( !ifp ) return;
108         while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
109         fclose(ifp);
110 }
111
112 static void bc_list_openfiles(int lines, FILE *ofp, const char *fmt,...)
113 {
114         va_list ap;    va_start(ap, fmt);
115         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
116         va_end(ap);
117         DIR *dir  = opendir(bfr);
118         if( !dir ) return;
119         struct dirent64 *dent;
120         while( --lines >= 0 && (dent = readdir64(dir)) ) {
121                 const char *fn = dent->d_name;
122                 fprintf(ofp, "%s", fn);
123                 char path[BCTEXTLEN], link[BCTEXTLEN];
124                 struct stat st;
125                 snprintf(path, sizeof(path), "%s/%s", bfr, fn);
126                 if( !stat(path,&st) ) {
127                         int typ = 0;
128                         if( S_ISREG(st.st_mode) )       typ = ' ';
129                         else if( S_ISDIR(st.st_mode) )  typ = 'd';
130                         else if( S_ISBLK(st.st_mode) )  typ = 'b';
131                         else if( S_ISCHR(st.st_mode) )  typ = 'c';
132                         else if( S_ISFIFO(st.st_mode) ) typ = 'f';
133                         else if( S_ISLNK(st.st_mode) )  typ = 'l';
134                         else if( S_ISSOCK(st.st_mode) ) typ = 's';
135                         if( typ ) fprintf(ofp, "\t%c", typ);
136                         fprintf(ofp, "\tsize %jd", st.st_size);
137                         int len = readlink(path, link, sizeof(link)-1);
138                         if( len > 0 ) {
139                                 link[len] = 0;
140                                 fprintf(ofp, "\t-> %s", link);
141                         }
142                 }
143                 snprintf(path, sizeof(path), "%sinfo/%s", bfr, fn);
144                 FILE *fp = fopen(path,"r");  int64_t pos;
145                 if( fp ) {
146                         while( fgets(link, sizeof(link), fp) ) {
147                                 if( sscanf(link, "pos:%jd", &pos) == 1 ) {
148                                         fprintf(ofp, "\tpos: %jd", pos);
149                                         break;
150                                 }
151                         }
152                         fclose(fp);
153                 }
154                 fprintf(ofp, "\n");
155         }
156         closedir(dir);
157 }
158
159 // Can't use Mutex because it would be recursive
160 static pthread_mutex_t *handler_lock = 0;
161
162
163 static const char* signal_titles[] =
164 {
165         "NULL",
166         "SIGHUP",
167         "SIGINT",
168         "SIGQUIT",
169         "SIGILL",
170         "SIGTRAP",
171         "SIGABRT",
172         "SIGBUS",
173         "SIGFPE",
174         "SIGKILL",
175         "SIGUSR1",
176         "SIGSEGV",
177         "SIGUSR2",
178         "SIGPIPE",
179         "SIGALRM",
180         "SIGTERM"
181 };
182
183 void BC_Signals::dump_stack(FILE *fp)
184 {
185         void *buffer[256];
186         int total = backtrace (buffer, 256);
187         char **result = backtrace_symbols (buffer, total);
188         fprintf(fp, "BC_Signals::dump_stack\n");
189         for(int i = 0; i < total; i++)
190         {
191                 fprintf(fp, "%s\n", result[i]);
192         }
193 }
194
195 // Kill subprocesses
196 void BC_Signals::kill_subs()
197 {
198 // List /proc directory
199         struct dirent64 *new_filename;
200         struct stat ostat;
201         char path[BCTEXTLEN], string[BCTEXTLEN];
202         DIR *dirstream = opendir("/proc");
203         if( !dirstream ) return;
204         pid_t ppid = getpid();
205
206         while( (new_filename = readdir64(dirstream)) != 0 ) {
207                 char *ptr = new_filename->d_name;
208                 while( *ptr && *ptr != '.' && !isalpha(*ptr) ) ++ptr;
209 // All digits are numbers
210                 if( *ptr ) continue;
211
212 // Must be a directory
213                 sprintf(path, "/proc/%s", new_filename->d_name);
214                 if( stat(path, &ostat) ) continue;
215                 if( !S_ISDIR(ostat.st_mode) ) continue;
216                 strcat(path, "/stat");
217                 FILE *fd = fopen(path, "r");
218                 if( !fd ) continue;
219                 while( !feof(fd) && fgetc(fd)!=')' );
220 //printf("kill_subs %d %d\n", __LINE__, c);
221                 for( int sp=2; !feof(fd) && sp>0; )
222                         if( fgetc(fd) == ' ' ) --sp;
223 // Read in parent process
224                 for( ptr=string; !feof(fd) && (*ptr=fgetc(fd))!=' '; ++ptr );
225                 if( (*ptr=fgetc(fd)) == ' ' ) break;
226                 *ptr = 0;
227
228 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
229 // __LINE__, atoi(new_filename->d_name), getpid(), atoi(string));
230                 int parent_process = atoi(string);
231 // Kill if we're the parent
232                 if( ppid == parent_process ) {
233                         int child_process = atoi(new_filename->d_name);
234 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
235                         kill(child_process, SIGKILL);
236                 }
237                 fclose(fd);
238         }
239 }
240
241 static void signal_entry(int signum)
242 {
243         signal(signum, SIG_DFL);
244
245         pthread_mutex_lock(handler_lock);
246         int done = signal_done;
247         signal_done = 1;
248         pthread_mutex_unlock(handler_lock);
249         if( done ) exit(0);
250
251         printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
252                 signal_titles[signum], getpid(), execution_table.size);
253
254         BC_Signals::kill_subs();
255         BC_Trace::dump_traces();
256         BC_Trace::dump_locks();
257         BC_Trace::dump_buffers();
258         BC_Trace::dump_shm_stats();
259         BC_Trace::delete_temps();
260
261 // Call user defined signal handler
262         BC_Signals::global_signals->signal_handler(signum);
263
264         abort();
265 }
266
267 static void signal_entry_recoverable(int signum)
268 {
269         printf("signal_entry_recoverable: got %s my pid=%d\n",
270                 signal_titles[signum],
271                 getpid());
272 }
273
274 // used to terminate child processes when program terminates
275 static void handle_exit(int signum)
276 {
277 //printf("child %d exit\n", getpid());
278         exit(0);
279 }
280
281 void BC_Signals::set_sighup_exit(int enable)
282 {
283         if( enable ) {
284 // causes SIGHUP to be generated when parent dies
285                 signal(SIGHUP, handle_exit);
286                 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
287 // prevents ^C from signalling child when attached to gdb
288                 setpgid(0, 0);
289                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
290         }
291         else {
292                 signal(SIGHUP, signal_entry);
293                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
294         }
295 }
296
297 BC_Signals::BC_Signals()
298 {
299 }
300 BC_Signals::~BC_Signals()
301 {
302   BC_CModels::bcxfer_stop_slicers();
303 }
304
305
306 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
307 {
308         char string[1024];
309         XGetErrorText(event->display, event->error_code, string, 1024);
310         fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
311                 event->error_code, event->request_code, event->minor_code,
312                 (int64_t)event->resourceid, string);
313         return 0;
314 }
315
316
317 void BC_Signals::initialize(const char *trap_path)
318 {
319         BC_Signals::global_signals = this;
320         BC_Trace::global_trace = this;
321         set_trap_path(trap_path);
322         handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
323         pthread_mutex_init(handler_lock, 0);
324         old_err_handler = XSetErrorHandler(x_error_handler);
325         initialize2();
326 }
327
328 void BC_Signals::terminate()
329 {
330         BC_Signals::global_signals = 0;
331         BC_Trace::global_trace = 0;
332         uncatch_segv();  uncatch_intr();
333         signal(SIGHUP, SIG_DFL);
334         signal(SIGINT, SIG_DFL);
335         signal(SIGQUIT, SIG_DFL);
336         signal(SIGTERM, SIG_DFL);
337         signal(SIGFPE, SIG_DFL);
338         signal(SIGPIPE, SIG_DFL);
339         signal(SIGUSR2, SIG_DFL);
340         XSetErrorHandler(old_err_handler);
341 }
342
343 // kill SIGUSR2
344 void BC_Signals::signal_dump(int signum)
345 {
346         BC_KeyboardHandler::kill_grabs();
347         dump();
348         signal(SIGUSR2, signal_dump);
349 }
350
351
352
353
354
355
356 void BC_Signals::initialize2()
357 {
358         signal(SIGHUP, signal_entry);
359         signal(SIGINT, signal_entry);
360         signal(SIGQUIT, signal_entry);
361         // SIGKILL cannot be stopped
362         // signal(SIGKILL, signal_entry);
363         catch_segv();
364         signal(SIGTERM, signal_entry);
365         signal(SIGFPE, signal_entry);
366         signal(SIGPIPE, signal_entry_recoverable);
367         signal(SIGUSR2, signal_dump);
368 }
369
370
371 void BC_Signals::signal_handler(int signum)
372 {
373 printf("BC_Signals::signal_handler\n");
374 //      exit(0);
375 }
376
377 const char* BC_Signals::sig_to_str(int number)
378 {
379         return signal_titles[number];
380 }
381
382
383 #include <ucontext.h>
384 #include <sys/wait.h>
385 #include "thread.h"
386
387 #if __i386__
388 #define IP eip
389 #endif
390 #if __x86_64__
391 #define IP rip
392 #endif
393 #ifndef IP
394 #error gotta have IP
395 #endif
396
397
398 static void handle_dump(int n, siginfo_t * info, void *sc)
399 {
400         uncatch_segv();  uncatch_intr();
401         signal(SIGSEGV, SIG_DFL);
402         signal(SIGINT, SIG_DFL);
403         // gotta be root, or the dump is worthless
404         int uid = getuid();
405 // it is not necessary to be root if ptrace is allowed via:
406 // echo 0 > /proc/sys/kernel/yama/ptrace_scope (usually set to 1)
407 //      if( uid != 0 ) return;
408         ucontext_t *uc = (ucontext_t *)sc;
409         int pid = getpid(), tid = gettid();
410         struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
411         uint8_t *ip = (uint8_t *)c->IP;
412         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
413                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
414                 (void*)ip, pid, tid);
415         FILE *fp = 0;
416         char fn[PATH_MAX];
417         if( BC_Signals::trap_path ) {
418                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
419                 fp = fopen(fn,"w");
420         }
421         if( fp ) {
422                 fprintf(stderr,"writing debug data to %s\n", fn);
423                 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
424                         n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
425                         (void*)c->IP, pid, tid);
426         }
427         else {
428                 strcpy(fn, "stdout");
429                 fp = stdout;
430         }
431         time_t t;  time(&t);
432         fprintf(fp,"created on %s", ctime(&t));
433         struct passwd *pw = getpwuid(uid);
434         if( pw ) {
435                 fprintf(fp,"        by %d:%d %s(%s)\n",
436                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
437         }
438         fprintf(fp,"\nCPUS: %d\n",   BC_Resources::get_machine_cpus());
439         fprintf(fp,"\nCPUINFO:\n");  bc_copy_textfile(32, fp,"/proc/cpuinfo");
440         fprintf(fp,"\nTHREADS:\n");  BC_Trace::dump_threads(fp);
441         fprintf(fp,"\nTRACES:\n");   BC_Trace::dump_traces(fp);
442         fprintf(fp,"\nLOCKS:\n");    BC_Trace::dump_locks(fp);
443         fprintf(fp,"\nBUFFERS:\n");  BC_Trace::dump_buffers(fp);
444         fprintf(fp,"\nSHMMEM:\n");   BC_Trace::dump_shm_stats(fp);
445         if( BC_Signals::trap_hook ) {
446                 fprintf(fp,"\nMAIN HOOK:\n");
447                 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
448         }
449         fprintf(fp,"\nVERSION:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/version");
450         fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
451         fprintf(fp,"\nSTATUS:\n");   bc_copy_textfile(INT_MAX, fp,"/proc/%d/status",pid);
452         fprintf(fp,"\nFD:\n");       bc_list_openfiles(INT_MAX, fp,"/proc/%d/fd", pid);
453         fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
454         char proc_mem[64];
455         if( tid > 0 && tid != pid )
456                 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
457         else
458                 sprintf(proc_mem,"/proc/%d/mem",pid);
459         int pfd = open(proc_mem,O_RDONLY);
460         if( pfd >= 0 ) {
461                 fprintf(fp,"\nCODE:\n");
462                 for( int i=-32; i<32; ) {
463                         uint8_t v;  void *vp = (void *)(ip + i);
464                         if( !(i & 7) ) fprintf(fp,"%p:  ", vp);
465                         if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
466                         fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
467                         if( !(++i & 7) ) fprintf(fp,"\n");
468                 }
469                 fprintf(fp,"\n");
470                 close(pfd);
471         }
472         else
473                 fprintf(fp,"err opening: %s, %m\n", proc_mem);
474
475         fprintf(fp,"\n\n");
476         if( fp != stdout ) fclose(fp);
477         char cmd[1024], *cp = cmd;
478         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
479                 "-ex \"thread apply all info registers\" "
480                 "-ex \"thread apply all bt full\" "
481                 "-ex \"quit\"", pid, pid);
482         if( fp != stdout )
483                 cp += sprintf(cp," >> \"%s\"", fn);
484         cp += sprintf(cp," 2>&1");
485 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
486         pid = vfork();
487         if( pid < 0 ) {
488                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
489                 return;
490         }
491         if( pid > 0 ) {
492                 waitpid(pid,0,0);
493                 fprintf(stderr,"** dump complete\n");
494                 return;
495         }
496         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
497         execvp(argv[0], &argv[0]);
498 }
499