glx ply3d fixes, async_gui updates for edits, dflt vs auto kfrm fix, mask mode fix...
[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 // Can't use Mutex because it would be recursive
113 static pthread_mutex_t *handler_lock = 0;
114
115
116 static const char* signal_titles[] =
117 {
118         "NULL",
119         "SIGHUP",
120         "SIGINT",
121         "SIGQUIT",
122         "SIGILL",
123         "SIGTRAP",
124         "SIGABRT",
125         "SIGBUS",
126         "SIGFPE",
127         "SIGKILL",
128         "SIGUSR1",
129         "SIGSEGV",
130         "SIGUSR2",
131         "SIGPIPE",
132         "SIGALRM",
133         "SIGTERM"
134 };
135
136 void BC_Signals::dump_stack(FILE *fp)
137 {
138         void *buffer[256];
139         int total = backtrace (buffer, 256);
140         char **result = backtrace_symbols (buffer, total);
141         fprintf(fp, "BC_Signals::dump_stack\n");
142         for(int i = 0; i < total; i++)
143         {
144                 fprintf(fp, "%s\n", result[i]);
145         }
146 }
147
148 // Kill subprocesses
149 void BC_Signals::kill_subs()
150 {
151 // List /proc directory
152         struct dirent64 *new_filename;
153         struct stat ostat;
154         char path[BCTEXTLEN], string[BCTEXTLEN];
155         DIR *dirstream = opendir("/proc");
156         if( !dirstream ) return;
157         pid_t ppid = getpid();
158
159         while( (new_filename = readdir64(dirstream)) != 0 ) {
160                 char *ptr = new_filename->d_name;
161                 while( *ptr && *ptr != '.' && !isalpha(*ptr) ) ++ptr;
162 // All digits are numbers
163                 if( *ptr ) continue;
164
165 // Must be a directory
166                 sprintf(path, "/proc/%s", new_filename->d_name);
167                 if( stat(path, &ostat) ) continue;
168                 if( !S_ISDIR(ostat.st_mode) ) continue;
169                 strcat(path, "/stat");
170                 FILE *fd = fopen(path, "r");
171                 if( !fd ) continue;
172                 while( !feof(fd) && fgetc(fd)!=')' );
173 //printf("kill_subs %d %d\n", __LINE__, c);
174                 for( int sp=2; !feof(fd) && sp>0; )
175                         if( fgetc(fd) == ' ' ) --sp;
176 // Read in parent process
177                 for( ptr=string; !feof(fd) && (*ptr=fgetc(fd))!=' '; ++ptr );
178                 if( (*ptr=fgetc(fd)) == ' ' ) break;
179                 *ptr = 0;
180
181 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
182 // __LINE__, atoi(new_filename->d_name), getpid(), atoi(string));
183                 int parent_process = atoi(string);
184 // Kill if we're the parent
185                 if( ppid == parent_process ) {
186                         int child_process = atoi(new_filename->d_name);
187 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
188                         kill(child_process, SIGKILL);
189                 }
190                 fclose(fd);
191         }
192 }
193
194 static void signal_entry(int signum)
195 {
196         signal(signum, SIG_DFL);
197
198         pthread_mutex_lock(handler_lock);
199         int done = signal_done;
200         signal_done = 1;
201         pthread_mutex_unlock(handler_lock);
202         if( done ) exit(0);
203
204         printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
205                 signal_titles[signum], getpid(), execution_table.size);
206
207         BC_Signals::kill_subs();
208         BC_Trace::dump_traces();
209         BC_Trace::dump_locks();
210         BC_Trace::dump_buffers();
211         BC_Trace::dump_shm_stats();
212         BC_Trace::delete_temps();
213
214 // Call user defined signal handler
215         BC_Signals::global_signals->signal_handler(signum);
216
217         abort();
218 }
219
220 static void signal_entry_recoverable(int signum)
221 {
222         printf("signal_entry_recoverable: got %s my pid=%d\n",
223                 signal_titles[signum],
224                 getpid());
225 }
226
227 // used to terminate child processes when program terminates
228 static void handle_exit(int signum)
229 {
230 //printf("child %d exit\n", getpid());
231         exit(0);
232 }
233
234 void BC_Signals::set_sighup_exit(int enable)
235 {
236         if( enable ) {
237 // causes SIGHUP to be generated when parent dies
238                 signal(SIGHUP, handle_exit);
239                 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
240 // prevents ^C from signalling child when attached to gdb
241                 setpgid(0, 0);
242                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
243         }
244         else {
245                 signal(SIGHUP, signal_entry);
246                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
247         }
248 }
249
250 BC_Signals::BC_Signals()
251 {
252 }
253 BC_Signals::~BC_Signals()
254 {
255   BC_CModels::bcxfer_stop_slicers();
256 }
257
258
259 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
260 {
261         char string[1024];
262         XGetErrorText(event->display, event->error_code, string, 1024);
263         fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
264                 event->error_code, event->request_code, event->minor_code,
265                 (int64_t)event->resourceid, string);
266         return 0;
267 }
268
269
270 void BC_Signals::initialize(const char *trap_path)
271 {
272         BC_Signals::global_signals = this;
273         BC_Trace::global_trace = this;
274         set_trap_path(trap_path);
275         handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
276         pthread_mutex_init(handler_lock, 0);
277         old_err_handler = XSetErrorHandler(x_error_handler);
278         initialize2();
279 }
280
281 void BC_Signals::terminate()
282 {
283         BC_Signals::global_signals = 0;
284         BC_Trace::global_trace = 0;
285         uncatch_segv();  uncatch_intr();
286         signal(SIGHUP, SIG_DFL);
287         signal(SIGINT, SIG_DFL);
288         signal(SIGQUIT, SIG_DFL);
289         signal(SIGTERM, SIG_DFL);
290         signal(SIGFPE, SIG_DFL);
291         signal(SIGPIPE, SIG_DFL);
292         signal(SIGUSR2, SIG_DFL);
293         XSetErrorHandler(old_err_handler);
294 }
295
296 // kill SIGUSR2
297 void BC_Signals::signal_dump(int signum)
298 {
299         BC_KeyboardHandler::kill_grabs();
300         dump();
301         signal(SIGUSR2, signal_dump);
302 }
303
304
305
306
307
308
309 void BC_Signals::initialize2()
310 {
311         signal(SIGHUP, signal_entry);
312         signal(SIGINT, signal_entry);
313         signal(SIGQUIT, signal_entry);
314         // SIGKILL cannot be stopped
315         // signal(SIGKILL, signal_entry);
316         catch_segv();
317         signal(SIGTERM, signal_entry);
318         signal(SIGFPE, signal_entry);
319         signal(SIGPIPE, signal_entry_recoverable);
320         signal(SIGUSR2, signal_dump);
321 }
322
323
324 void BC_Signals::signal_handler(int signum)
325 {
326 printf("BC_Signals::signal_handler\n");
327 //      exit(0);
328 }
329
330 const char* BC_Signals::sig_to_str(int number)
331 {
332         return signal_titles[number];
333 }
334
335
336 #include <ucontext.h>
337 #include <sys/wait.h>
338 #include "thread.h"
339
340 #if __i386__
341 #define IP eip
342 #endif
343 #if __x86_64__
344 #define IP rip
345 #endif
346 #ifndef IP
347 #error gotta have IP
348 #endif
349
350
351 static void handle_dump(int n, siginfo_t * info, void *sc)
352 {
353         uncatch_segv();  uncatch_intr();
354         signal(SIGSEGV, SIG_DFL);
355         signal(SIGINT, SIG_DFL);
356         // gotta be root, or the dump is worthless
357         int uid = getuid();
358 // it is not necessary to be root if ptrace is allowed via:
359 // echo 0 > /proc/sys/kernel/yama/ptrace_scope (usually set to 1)
360 //      if( uid != 0 ) return;
361         ucontext_t *uc = (ucontext_t *)sc;
362         int pid = getpid(), tid = gettid();
363         struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
364         uint8_t *ip = (uint8_t *)c->IP;
365         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
366                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
367                 (void*)ip, pid, tid);
368         FILE *fp = 0;
369         char fn[PATH_MAX];
370         if( BC_Signals::trap_path ) {
371                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
372                 fp = fopen(fn,"w");
373         }
374         if( fp ) {
375                 fprintf(stderr,"writing debug data to %s\n", fn);
376                 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
377                         n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
378                         (void*)c->IP, pid, tid);
379         }
380         else {
381                 strcpy(fn, "stdout");
382                 fp = stdout;
383         }
384         time_t t;  time(&t);
385         fprintf(fp,"created on %s", ctime(&t));
386         struct passwd *pw = getpwuid(uid);
387         if( pw ) {
388                 fprintf(fp,"        by %d:%d %s(%s)\n",
389                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
390         }
391         fprintf(fp,"\nCPUS: %d\n",   BC_Resources::get_machine_cpus());
392         fprintf(fp,"\nCPUINFO:\n");  bc_copy_textfile(32, fp,"/proc/cpuinfo");
393         fprintf(fp,"\nTHREADS:\n");  BC_Trace::dump_threads(fp);
394         fprintf(fp,"\nTRACES:\n");   BC_Trace::dump_traces(fp);
395         fprintf(fp,"\nLOCKS:\n");    BC_Trace::dump_locks(fp);
396         fprintf(fp,"\nBUFFERS:\n");  BC_Trace::dump_buffers(fp);
397         fprintf(fp,"\nSHMMEM:\n");   BC_Trace::dump_shm_stats(fp);
398         if( BC_Signals::trap_hook ) {
399                 fprintf(fp,"\nMAIN HOOK:\n");
400                 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
401         }
402         fprintf(fp,"\nVERSION:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/version");
403         fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
404         fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
405         char proc_mem[64];
406         if( tid > 0 && tid != pid )
407                 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
408         else
409                 sprintf(proc_mem,"/proc/%d/mem",pid);
410         int pfd = open(proc_mem,O_RDONLY);
411         if( pfd >= 0 ) {
412                 fprintf(fp,"\nCODE:\n");
413                 for( int i=-32; i<32; ) {
414                         uint8_t v;  void *vp = (void *)(ip + i);
415                         if( !(i & 7) ) fprintf(fp,"%p:  ", vp);
416                         if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
417                         fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
418                         if( !(++i & 7) ) fprintf(fp,"\n");
419                 }
420                 fprintf(fp,"\n");
421                 close(pfd);
422         }
423         else
424                 fprintf(fp,"err opening: %s, %m\n", proc_mem);
425
426         fprintf(fp,"\n\n");
427         if( fp != stdout ) fclose(fp);
428         char cmd[1024], *cp = cmd;
429         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
430                 "-ex \"thread apply all info registers\" "
431                 "-ex \"thread apply all bt full\" "
432                 "-ex \"quit\"", pid, pid);
433         if( fp != stdout )
434                 cp += sprintf(cp," >> \"%s\"", fn);
435         cp += sprintf(cp," 2>&1");
436 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
437         pid = vfork();
438         if( pid < 0 ) {
439                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
440                 return;
441         }
442         if( pid > 0 ) {
443                 waitpid(pid,0,0);
444                 fprintf(stderr,"** dump complete\n");
445                 return;
446         }
447         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
448         execvp(argv[0], &argv[0]);
449 }
450