ru.po from igor, intl tweaks, picon fixes for last chkin, snap motion event on btns
[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::delete_temps();
212
213 // Call user defined signal handler
214         BC_Signals::global_signals->signal_handler(signum);
215
216         abort();
217 }
218
219 static void signal_entry_recoverable(int signum)
220 {
221         printf("signal_entry_recoverable: got %s my pid=%d\n",
222                 signal_titles[signum],
223                 getpid());
224 }
225
226 // used to terminate child processes when program terminates
227 static void handle_exit(int signum)
228 {
229 //printf("child %d exit\n", getpid());
230         exit(0);
231 }
232
233 void BC_Signals::set_sighup_exit(int enable)
234 {
235         if( enable ) {
236 // causes SIGHUP to be generated when parent dies
237                 signal(SIGHUP, handle_exit);
238                 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
239 // prevents ^C from signalling child when attached to gdb
240                 setpgid(0, 0);
241                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
242         }
243         else {
244                 signal(SIGHUP, signal_entry);
245                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
246         }
247 }
248
249 BC_Signals::BC_Signals()
250 {
251 }
252 BC_Signals::~BC_Signals()
253 {
254   BC_CModels::bcxfer_stop_slicers();
255 }
256
257
258 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
259 {
260         char string[1024];
261         XGetErrorText(event->display, event->error_code, string, 1024);
262         fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
263                 event->error_code, event->request_code, event->minor_code,
264                 (int64_t)event->resourceid, string);
265         return 0;
266 }
267
268
269 void BC_Signals::initialize(const char *trap_path)
270 {
271         BC_Signals::global_signals = this;
272         BC_Trace::global_trace = this;
273         set_trap_path(trap_path);
274         handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
275         pthread_mutex_init(handler_lock, 0);
276         old_err_handler = XSetErrorHandler(x_error_handler);
277         initialize2();
278 }
279
280 void BC_Signals::terminate()
281 {
282         BC_Signals::global_signals = 0;
283         BC_Trace::global_trace = 0;
284         uncatch_segv();  uncatch_intr();
285         signal(SIGHUP, SIG_DFL);
286         signal(SIGINT, SIG_DFL);
287         signal(SIGQUIT, SIG_DFL);
288         signal(SIGTERM, SIG_DFL);
289         signal(SIGFPE, SIG_DFL);
290         signal(SIGPIPE, SIG_DFL);
291         signal(SIGUSR2, SIG_DFL);
292         XSetErrorHandler(old_err_handler);
293 }
294
295 // kill SIGUSR2
296 void BC_Signals::signal_dump(int signum)
297 {
298         BC_KeyboardHandler::kill_grabs();
299         dump();
300         signal(SIGUSR2, signal_dump);
301 }
302
303
304
305
306
307
308 void BC_Signals::initialize2()
309 {
310         signal(SIGHUP, signal_entry);
311         signal(SIGINT, signal_entry);
312         signal(SIGQUIT, signal_entry);
313         // SIGKILL cannot be stopped
314         // signal(SIGKILL, signal_entry);
315         catch_segv();
316         signal(SIGTERM, signal_entry);
317         signal(SIGFPE, signal_entry);
318         signal(SIGPIPE, signal_entry_recoverable);
319         signal(SIGUSR2, signal_dump);
320 }
321
322
323 void BC_Signals::signal_handler(int signum)
324 {
325 printf("BC_Signals::signal_handler\n");
326 //      exit(0);
327 }
328
329 const char* BC_Signals::sig_to_str(int number)
330 {
331         return signal_titles[number];
332 }
333
334
335 #include <ucontext.h>
336 #include <sys/wait.h>
337 #include "thread.h"
338
339 #if __i386__
340 #define IP eip
341 #endif
342 #if __x86_64__
343 #define IP rip
344 #endif
345 #ifndef IP
346 #error gotta have IP
347 #endif
348
349
350 static void handle_dump(int n, siginfo_t * info, void *sc)
351 {
352         uncatch_segv();  uncatch_intr();
353         signal(SIGSEGV, SIG_DFL);
354         signal(SIGINT, SIG_DFL);
355         // gotta be root, or the dump is worthless
356         int uid = getuid();
357         if( uid != 0 ) return;
358         ucontext_t *uc = (ucontext_t *)sc;
359         int pid = getpid(), tid = gettid();
360         struct sigcontext *c = (struct sigcontext *)&uc->uc_mcontext;
361         uint8_t *ip = (uint8_t *)c->IP;
362         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
363                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
364                 (void*)ip, pid, tid);
365         FILE *fp = 0;
366         char fn[PATH_MAX];
367         if( BC_Signals::trap_path ) {
368                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
369                 fp = fopen(fn,"w");
370         }
371         if( fp ) {
372                 fprintf(stderr,"writing debug data to %s\n", fn);
373                 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
374                         n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
375                         (void*)c->IP, pid, tid);
376         }
377         else {
378                 strcpy(fn, "stdout");
379                 fp = stdout;
380         }
381         time_t t;  time(&t);
382         fprintf(fp,"created on %s", ctime(&t));
383         struct passwd *pw = getpwuid(uid);
384         if( pw ) {
385                 fprintf(fp,"        by %d:%d %s(%s)\n",
386                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
387         }
388         fprintf(fp,"\nCPUS: %d\n",   BC_Resources::get_machine_cpus());
389         fprintf(fp,"\nCPUINFO:\n");  bc_copy_textfile(32, fp,"/proc/cpuinfo");
390         fprintf(fp,"\nTHREADS:\n");  BC_Trace::dump_threads(fp);
391         fprintf(fp,"\nTRACES:\n");   BC_Trace::dump_traces(fp);
392         fprintf(fp,"\nLOCKS:\n");    BC_Trace::dump_locks(fp);
393         fprintf(fp,"\nBUFFERS:\n");  BC_Trace::dump_buffers(fp);
394         if( BC_Signals::trap_hook ) {
395                 fprintf(fp,"\nMAIN HOOK:\n");
396                 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
397         }
398         fprintf(fp,"\nVERSION:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/version");
399         fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
400         fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
401         char proc_mem[64];
402         if( tid > 0 && tid != pid )
403                 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
404         else
405                 sprintf(proc_mem,"/proc/%d/mem",pid);
406         int pfd = open(proc_mem,O_RDONLY);
407         if( pfd >= 0 ) {
408                 fprintf(fp,"\nCODE:\n");
409                 for( int i=-32; i<32; ) {
410                         uint8_t v;  void *vp = (void *)(ip + i);
411                         if( !(i & 7) ) fprintf(fp,"%p:  ", vp);
412                         if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
413                         fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
414                         if( !(++i & 7) ) fprintf(fp,"\n");
415                 }
416                 fprintf(fp,"\n");
417                 close(pfd);
418         }
419         else
420                 fprintf(fp,"err opening: %s, %m\n", proc_mem);
421
422         fprintf(fp,"\n\n");
423         if( fp != stdout ) fclose(fp);
424         char cmd[1024], *cp = cmd;
425         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
426                 "-ex \"thread apply all info registers\" "
427                 "-ex \"thread apply all bt full\" "
428                 "-ex \"quit\"", pid, pid);
429         if( fp != stdout )
430                 cp += sprintf(cp," >> \"%s\"", fn);
431         cp += sprintf(cp," 2>&1");
432 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
433         pid = vfork();
434         if( pid < 0 ) {
435                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
436                 return;
437         }
438         if( pid > 0 ) {
439                 waitpid(pid,0,0);
440                 fprintf(stderr,"** dump complete\n");
441                 return;
442         }
443         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
444         execvp(argv[0], &argv[0]);
445 }
446