anonymous contribution for much improved ChromakeyHSV plugin menu with boxes to set...
[goodguy/cinelerra.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 #include "filesystem.h"
29
30 #include <ctype.h>
31 #include <dirent.h>
32 #ifndef NO_BTRACE
33 #include <execinfo.h>
34 #endif
35 #include <fcntl.h>
36 #include <pwd.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <sys/ioctl.h>
43 #ifndef NO_PRCTL
44 #include <sys/prctl.h>
45 #endif
46 #include <sys/types.h>
47
48 BC_Signals* BC_Signals::global_signals = 0;
49 static int signal_done = 0;
50
51 static struct sigaction old_segv = {0, }, old_intr = {0, };
52 static void handle_dump(int n, siginfo_t * info, void *sc);
53
54 const char *BC_Signals::trap_path = 0;
55 void *BC_Signals::trap_data = 0;
56 void (*BC_Signals::trap_hook)(FILE *fp, void *data) = 0;
57 bool BC_Signals::trap_sigsegv = false;
58 bool BC_Signals::trap_sigintr = false;
59
60 static void uncatch_sig(int sig, struct sigaction &old)
61 {
62         struct sigaction act;
63         sigaction(sig, &old, &act);
64         old.sa_handler = 0;
65 }
66
67 static void catch_sig(int sig, struct sigaction &old)
68 {
69         struct sigaction act;
70         memset(&act, 0, sizeof(act));
71         act.sa_sigaction = handle_dump;
72         act.sa_flags = SA_SIGINFO;
73         sigaction(sig, &act, (!old.sa_handler ? &old : 0));
74 }
75
76 static void uncatch_intr() { uncatch_sig(SIGINT, old_intr); }
77 static void catch_intr() { catch_sig(SIGINT, old_intr); }
78 static void uncatch_segv() { uncatch_sig(SIGSEGV, old_segv); }
79 static void catch_segv() { catch_sig(SIGSEGV, old_segv); }
80
81 void BC_Signals::set_trap_path(const char *path)
82 {
83         trap_path = path;
84 }
85
86 void BC_Signals::set_trap_hook(void (*hook)(FILE *fp, void *vp), void *data)
87 {
88         trap_data = data;
89         trap_hook = hook;
90 }
91
92 void BC_Signals::set_catch_segv(bool v) {
93         if( v == trap_sigsegv ) return;
94         if( v ) catch_segv();
95         else uncatch_segv();
96         trap_sigsegv = v;
97 }
98
99 void BC_Signals::set_catch_intr(bool v) {
100         if( v == trap_sigintr ) return;
101         if( v ) catch_intr();
102         else uncatch_intr();
103         trap_sigintr = v;
104 }
105
106 static void bc_copy_textfile(int lines, FILE *ofp, const char *fmt,...)
107 {
108         va_list ap;    va_start(ap, fmt);
109         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
110         va_end(ap);
111         FILE *ifp = fopen(bfr,"r");
112         if( !ifp ) return;
113         while( --lines >= 0 && fgets(bfr,sizeof(bfr),ifp) ) fputs(bfr,ofp);
114         fclose(ifp);
115 }
116
117 static void bc_list_openfiles(int lines, FILE *ofp, const char *fmt,...)
118 {
119         va_list ap;    va_start(ap, fmt);
120         char bfr[BCTEXTLEN];  vsnprintf(bfr, sizeof(bfr), fmt, ap);
121         va_end(ap);
122         DIR *dir  = opendir(bfr);
123         if( !dir ) return;
124         struct dirent64 *dent;
125         while( --lines >= 0 && (dent = readdir64(dir)) ) {
126                 const char *fn = dent->d_name;
127                 fprintf(ofp, "%s", fn);
128                 char path[BCTEXTLEN], link[BCTEXTLEN];
129                 struct stat st;
130                 snprintf(path, sizeof(path), "%s/%s", bfr, fn);
131                 if( !stat(path,&st) ) {
132                         int typ = 0;
133                         if( S_ISREG(st.st_mode) )       typ = ' ';
134                         else if( S_ISDIR(st.st_mode) )  typ = 'd';
135                         else if( S_ISBLK(st.st_mode) )  typ = 'b';
136                         else if( S_ISCHR(st.st_mode) )  typ = 'c';
137                         else if( S_ISFIFO(st.st_mode) ) typ = 'f';
138                         else if( S_ISLNK(st.st_mode) )  typ = 'l';
139                         else if( S_ISSOCK(st.st_mode) ) typ = 's';
140                         if( typ ) fprintf(ofp, "\t%c", typ);
141                         fprintf(ofp, "\tsize %jd", st.st_size);
142                         int len = readlink(path, link, sizeof(link)-1);
143                         if( len > 0 ) {
144                                 link[len] = 0;
145                                 fprintf(ofp, "\t-> %s", link);
146                         }
147                 }
148                 snprintf(path, sizeof(path), "%sinfo/%s", bfr, fn);
149                 FILE *fp = fopen(path,"r");  int64_t pos;
150                 if( fp ) {
151                         while( fgets(link, sizeof(link), fp) ) {
152                                 if( sscanf(link, "pos:%jd", &pos) == 1 ) {
153                                         fprintf(ofp, "\tpos: %jd", pos);
154                                         break;
155                                 }
156                         }
157                         fclose(fp);
158                 }
159                 fprintf(ofp, "\n");
160         }
161         closedir(dir);
162 }
163
164 // Can't use Mutex because it would be recursive
165 static pthread_mutex_t *handler_lock = 0;
166
167
168 static const char* signal_titles[] =
169 {
170         "NULL",
171         "SIGHUP",
172         "SIGINT",
173         "SIGQUIT",
174         "SIGILL",
175         "SIGTRAP",
176         "SIGABRT",
177         "SIGBUS",
178         "SIGFPE",
179         "SIGKILL",
180         "SIGUSR1",
181         "SIGSEGV",
182         "SIGUSR2",
183         "SIGPIPE",
184         "SIGALRM",
185         "SIGTERM"
186 };
187
188 void BC_Signals::dump_stack(FILE *fp)
189 {
190 #ifndef NO_BTRACE
191         void *buffer[256];
192         int total = backtrace (buffer, 256);
193         char **result = backtrace_symbols (buffer, total);
194         fprintf(fp, "BC_Signals::dump_stack\n");
195         for(int i = 0; i < total; i++)
196         {
197                 fprintf(fp, "%s\n", result[i]);
198         }
199 #endif
200 }
201
202 // Kill subprocesses
203 void BC_Signals::kill_subs()
204 {
205 // List /proc directory
206         struct dirent64 *new_filename;
207         struct stat ostat;
208         char path[BCTEXTLEN], string[BCTEXTLEN];
209         DIR *dirstream = opendir("/proc");
210         if( !dirstream ) return;
211         pid_t ppid = getpid();
212
213         while( (new_filename = readdir64(dirstream)) != 0 ) {
214                 char *ptr = new_filename->d_name;
215                 while( *ptr && *ptr != '.' && !isalpha(*ptr) ) ++ptr;
216 // All digits are numbers
217                 if( *ptr ) continue;
218
219 // Must be a directory
220                 sprintf(path, "/proc/%s", new_filename->d_name);
221                 if( stat(path, &ostat) ) continue;
222                 if( !S_ISDIR(ostat.st_mode) ) continue;
223                 strcat(path, "/stat");
224                 FILE *fd = fopen(path, "r");
225                 if( !fd ) continue;
226                 while( !feof(fd) && fgetc(fd)!=')' );
227 //printf("kill_subs %d %d\n", __LINE__, c);
228                 for( int sp=2; !feof(fd) && sp>0; )
229                         if( fgetc(fd) == ' ' ) --sp;
230 // Read in parent process
231                 for( ptr=string; !feof(fd) && (*ptr=fgetc(fd))!=' '; ++ptr );
232                 if( (*ptr=fgetc(fd)) == ' ' ) break;
233                 *ptr = 0;
234
235 // printf("kill_subs %d process=%d getpid=%d parent_process=%d\n",
236 // __LINE__, atoi(new_filename->d_name), getpid(), atoi(string));
237                 int parent_process = atoi(string);
238 // Kill if we're the parent
239                 if( ppid == parent_process ) {
240                         int child_process = atoi(new_filename->d_name);
241 //printf("kill_subs %d: process=%d\n", __LINE__, atoi(new_filename->d_name));
242                         kill(child_process, SIGKILL);
243                 }
244                 fclose(fd);
245         }
246 }
247
248 static void signal_entry(int signum)
249 {
250         signal(signum, SIG_DFL);
251
252         pthread_mutex_lock(handler_lock);
253         int done = signal_done;
254         signal_done = 1;
255         pthread_mutex_unlock(handler_lock);
256         if( done ) exit(0);
257
258         printf("signal_entry: got %s my pid=%d execution table size=%d:\n",
259                 signal_titles[signum], getpid(), execution_table.size);
260
261         BC_Signals::kill_subs();
262         BC_Trace::dump_traces();
263         BC_Trace::dump_locks();
264         BC_Trace::dump_buffers();
265         BC_Trace::delete_temps();
266
267 // Call user defined signal handler
268         BC_Signals::global_signals->signal_handler(signum);
269
270         abort();
271 }
272
273 static void signal_entry_recoverable(int signum)
274 {
275         printf("signal_entry_recoverable: got %s my pid=%d\n",
276                 signal_titles[signum],
277                 getpid());
278 }
279
280 #ifndef NO_PRCTL
281 // used to terminate child processes when program terminates
282 static void handle_exit(int signum)
283 {
284 //printf("child %d exit\n", getpid());
285         exit(0);
286 }
287 #endif
288
289 void BC_Signals::set_sighup_exit(int enable)
290 {
291 #ifndef NO_PRCTL
292         if( enable ) {
293 // causes SIGHUP to be generated when parent dies
294                 signal(SIGHUP, handle_exit);
295                 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
296 // prevents ^C from signalling child when attached to gdb
297                 setpgid(0, 0);
298                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
299         }
300         else {
301                 signal(SIGHUP, signal_entry);
302                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
303         }
304 #endif
305 }
306
307 BC_Signals::BC_Signals()
308 {
309 }
310 BC_Signals::~BC_Signals()
311 {
312   BC_CModels::bcxfer_stop_slicers();
313 }
314
315
316 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
317 {
318         char string[1024];
319         XGetErrorText(event->display, event->error_code, string, 1024);
320         fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
321                 event->error_code, event->request_code, event->minor_code,
322                 (int64_t)event->resourceid, string);
323         return 0;
324 }
325
326
327 void BC_Signals::initialize(const char *trap_path)
328 {
329         BC_Signals::global_signals = this;
330         BC_Trace::global_trace = this;
331         set_trap_path(trap_path);
332         handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
333         pthread_mutex_init(handler_lock, 0);
334         old_err_handler = XSetErrorHandler(x_error_handler);
335         initialize2();
336 }
337
338 void BC_Signals::terminate()
339 {
340         BC_Signals::global_signals = 0;
341         BC_Trace::global_trace = 0;
342         uncatch_segv();  uncatch_intr();
343         signal(SIGHUP, SIG_DFL);
344         signal(SIGINT, SIG_DFL);
345         signal(SIGQUIT, SIG_DFL);
346         signal(SIGTERM, SIG_DFL);
347         signal(SIGFPE, SIG_DFL);
348         signal(SIGPIPE, SIG_DFL);
349         signal(SIGUSR2, SIG_DFL);
350         XSetErrorHandler(old_err_handler);
351 }
352
353 // kill SIGUSR2
354 void BC_Signals::signal_dump(int signum)
355 {
356         BC_KeyboardHandler::kill_grabs();
357         dump();
358         signal(SIGUSR2, signal_dump);
359 }
360
361
362
363
364
365
366 void BC_Signals::initialize2()
367 {
368         signal(SIGHUP, signal_entry);
369         signal(SIGINT, signal_entry);
370         signal(SIGQUIT, signal_entry);
371         // SIGKILL cannot be stopped
372         // signal(SIGKILL, signal_entry);
373         catch_segv();
374         signal(SIGTERM, signal_entry);
375         signal(SIGFPE, signal_entry);
376         signal(SIGPIPE, signal_entry_recoverable);
377         signal(SIGUSR2, signal_dump);
378 }
379
380
381 void BC_Signals::signal_handler(int signum)
382 {
383 printf("BC_Signals::signal_handler\n");
384 //      exit(0);
385 }
386
387 const char* BC_Signals::sig_to_str(int number)
388 {
389         return signal_titles[number];
390 }
391
392
393 #ifndef NO_CTX
394 #include <ucontext.h>
395 #include <sys/wait.h>
396 #include "thread.h"
397
398 #if __i386__
399 #define IP eip
400 #define sigregs_t sigcontext
401
402 static void reg_dump(FILE *fp,sigregs_t *rp)
403 {
404         fprintf(fp,"REGS:\n");
405         fprintf(fp,"  gs: %04x:%04x\n", rp->gs,rp->__gsh);
406         fprintf(fp,"  fs: %04x:%04x\n", rp->fs,rp->__fsh);
407         fprintf(fp,"  es: %04x:%04x\n", rp->es,rp->__esh);
408         fprintf(fp,"  ds: %04x:%04x\n", rp->ds,rp->__dsh);
409         fprintf(fp," edi: %14p %d\n", (void*)rp->edi,rp->edi);
410         fprintf(fp," esi: %14p %d\n", (void*)rp->esi,rp->esi);
411         fprintf(fp," ebp: %14p %d\n", (void*)rp->ebp,rp->ebp);
412         fprintf(fp," esp: %14p %d\n", (void*)rp->esp,rp->esp);
413         fprintf(fp," ebx: %14p %d\n", (void*)rp->ebx,rp->ebx);
414         fprintf(fp," edx: %14p %d\n", (void*)rp->edx,rp->edx);
415         fprintf(fp," ecx: %14p %d\n", (void*)rp->ecx,rp->ecx);
416         fprintf(fp," eax: %14p %d\n", (void*)rp->eax,rp->eax);
417         fprintf(fp," trapno: %14p %d\n", (void*)rp->trapno,rp->trapno);
418         fprintf(fp," err: %14p %d\n", (void*)rp->err,rp->err);
419         fprintf(fp," eip: %14p %d\n", (void*)rp->eip,rp->eip);
420         fprintf(fp," cs: %04xd : %04x\n", rp->cs,rp->__csh);
421         fprintf(fp," eflags: %14p %d\n", (void*)rp->eflags,rp->eflags);
422         fprintf(fp," esp_at_signal: %p %d\n", (void*)rp->esp_at_signal,rp->esp_at_signal);
423         fprintf(fp," ss: %04xd : %04x\n", rp->ss,rp->__ssh);
424         fprintf(fp," oldmask: %14p %d\n", (void*)rp->oldmask,rp->oldmask);
425         fprintf(fp," cr2: %14p %d\n", (void*)rp->cr2,rp->cr2);
426         fprintf(fp,"\n");
427 }
428 #endif
429
430 #if __x86_64__
431 #define IP rip
432 #define sigregs_t sigcontext
433
434 static void reg_dump(FILE *fp,sigregs_t *rp)
435 {
436         fprintf(fp,"REGS:\n");
437         fprintf(fp,"  r8: %20p %jd\n", (void*)rp->r8,rp->r8);
438         fprintf(fp,"  r9: %20p %jd\n", (void*)rp->r9,rp->r9);
439         fprintf(fp," r10: %20p %jd\n", (void*)rp->r10,rp->r10);
440         fprintf(fp," r11: %20p %jd\n", (void*)rp->r11,rp->r11);
441         fprintf(fp," r12: %20p %jd\n", (void*)rp->r12,rp->r12);
442         fprintf(fp," r13: %20p %jd\n", (void*)rp->r13,rp->r13);
443         fprintf(fp," r14: %20p %jd\n", (void*)rp->r14,rp->r14);
444         fprintf(fp," r15: %20p %jd\n", (void*)rp->r15,rp->r15);
445         fprintf(fp," rdi: %20p %jd\n", (void*)rp->rdi,rp->rdi);
446         fprintf(fp," rsi: %20p %jd\n", (void*)rp->rsi,rp->rsi);
447         fprintf(fp," rbp: %20p %jd\n", (void*)rp->rbp,rp->rbp);
448         fprintf(fp," rbx: %20p %jd\n", (void*)rp->rbx,rp->rbx);
449         fprintf(fp," rdx: %20p %jd\n", (void*)rp->rdx,rp->rdx);
450         fprintf(fp," rax: %20p %jd\n", (void*)rp->rax,rp->rax);
451         fprintf(fp," rcx: %20p %jd\n", (void*)rp->rcx,rp->rcx);
452         fprintf(fp," rsp: %20p %jd\n", (void*)rp->rsp,rp->rsp);
453         fprintf(fp," rip: %20p %jd\n", (void*)rp->rip,rp->rip);
454         fprintf(fp," eflags: %14p %jd\n", (void*)rp->eflags,rp->eflags);
455         fprintf(fp,"  cs: %04x\n", rp->cs);
456         fprintf(fp,"  gs: %04x\n", rp->gs);
457         fprintf(fp,"  fs: %04x\n", rp->fs);
458         fprintf(fp," err: %20p %jd\n", (void*)rp->err,rp->err);
459         fprintf(fp," trapno: %20p %jd\n", (void*)rp->trapno,rp->trapno);
460         fprintf(fp," oldmask: %20p %jd\n", (void*)rp->oldmask,rp->oldmask);
461         fprintf(fp," cr2: %20p %jd\n", (void*)rp->cr2,rp->cr2);
462         fprintf(fp,"\n");
463 }
464
465 #endif
466
467 #if __powerpc__ || __powerpc64__ || __powerpc64le__
468 #include <asm/ptrace.h>
469 #define IP nip
470 #define sigregs_t pt_regs
471 static void reg_dump(FILE *fp,sigregs_t *rp) {}
472 #endif
473
474 #ifndef IP
475 #error gotta have IP
476 #endif
477
478 // HAVE_CTX
479 #endif
480
481 static void handle_dump(int n, siginfo_t * info, void *sc)
482 {
483         uncatch_segv();  uncatch_intr();
484         signal(SIGSEGV, SIG_DFL);
485         signal(SIGINT, SIG_DFL);
486         // gotta be root, or the dump is worthless
487         int uid = getuid();
488 // it is not necessary to be root if ptrace is allowed via:
489 // echo 0 > /proc/sys/kernel/yama/ptrace_scope (usually set to 1)
490 //      if( uid != 0 ) return;
491         int pid = getpid(), tid = gettid();
492         void *ip = 0;
493 #ifndef NO_CTX
494         ucontext_t *uc = (ucontext_t *)sc;
495         struct sigregs_t *c = (struct sigregs_t *)&uc->uc_mcontext;
496         ip = (void *)c->IP;
497 #endif
498         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
499                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
500                 ip, pid, tid);
501         FILE *fp = 0;
502         char fn[PATH_MAX];
503         if( BC_Signals::trap_path ) {
504                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
505                 fp = fopen(fn,"w");
506         }
507         if( fp ) {
508                 fprintf(stderr,"writing debug data to %s\n", fn);
509                 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
510                         n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
511                         ip, pid, tid);
512         }
513         else {
514                 strcpy(fn, "stdout");
515                 fp = stdout;
516         }
517         time_t t;  time(&t);
518         fprintf(fp,"created on %s", ctime(&t));
519         struct passwd *pw = getpwuid(uid);
520         if( pw ) {
521                 fprintf(fp,"        by %d:%d %s(%s)\n",
522                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
523         }
524         fprintf(fp,"\nOS:\n");  bc_copy_textfile(16, fp,"/etc/os-release");
525         fprintf(fp,"\nCPUS: %d\n",   BC_Resources::get_machine_cpus());
526         fprintf(fp,"\nCPUINFO:\n");  bc_copy_textfile(32, fp,"/proc/cpuinfo");
527         fprintf(fp,"\nTHREADS:\n");  BC_Trace::dump_threads(fp);
528         fprintf(fp,"\nTRACES:\n");   BC_Trace::dump_traces(fp);
529         fprintf(fp,"\nLOCKS:\n");    BC_Trace::dump_locks(fp);
530         fprintf(fp,"\nBUFFERS:\n");  BC_Trace::dump_buffers(fp);
531         fprintf(fp,"\nSHMMEM:\n");   BC_Trace::dump_shm_stats(fp);
532         if( BC_Signals::trap_hook ) {
533                 fprintf(fp,"\nMAIN HOOK:\n");
534                 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
535         }
536         fprintf(fp,"\nVERSION:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/version");
537         fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
538         fprintf(fp,"\nSTATUS:\n");   bc_copy_textfile(INT_MAX, fp,"/proc/%d/status",pid);
539         fprintf(fp,"\nFD:\n");       bc_list_openfiles(INT_MAX, fp,"/proc/%d/fd", pid);
540         fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
541 #ifndef NO_CTX
542         char proc_mem[64];
543         if( tid > 0 && tid != pid )
544                 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
545         else
546                 sprintf(proc_mem,"/proc/%d/mem",pid);
547         int pfd = open(proc_mem,O_RDONLY);
548         if( pfd >= 0 ) {
549                 fprintf(fp,"\nCODE:\n");
550                 for( int i=-32; i<32; ) {
551                         uint8_t v;  void *vp = (void *)((char*)ip + i);
552                         if( !(i & 7) ) fprintf(fp,"%p:  ", vp);
553                         if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
554                         fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
555                         if( !(++i & 7) ) fprintf(fp,"\n");
556                 }
557                 fprintf(fp,"\n");
558                 close(pfd);
559         }
560         else
561                 fprintf(fp,"err opening: %s, %m\n", proc_mem);
562         reg_dump(fp, c);
563 #endif
564         fprintf(fp,"\n\n");
565         if( fp != stdout ) fclose(fp);
566 #ifndef NO_GDB
567         char cmd[1024], *cp = cmd;
568         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
569                 "-ex \"thread apply all info registers\" "
570                 "-ex \"thread apply all bt full\" "
571                 "-ex \"quit\"", pid, pid);
572         if( fp != stdout )
573                 cp += sprintf(cp," >> \"%s\"", fn);
574         cp += sprintf(cp," 2>&1");
575 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
576         pid = vfork();
577         if( pid < 0 ) {
578                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
579                 return;
580         }
581         if( pid > 0 ) {
582                 waitpid(pid,0,0);
583                 fprintf(stderr,"** dump complete\n");
584                 return;
585         }
586         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
587         execvp(argv[0], &argv[0]);
588 #endif
589 }
590