remove Features5, rework gradient plugin, fix paste_edl track title bug, gl_probe...
[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
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         trap_sigsegv = v;
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         trap_sigintr = v;
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::delete_temps();
259
260 // Call user defined signal handler
261         BC_Signals::global_signals->signal_handler(signum);
262
263         abort();
264 }
265
266 static void signal_entry_recoverable(int signum)
267 {
268         printf("signal_entry_recoverable: got %s my pid=%d\n",
269                 signal_titles[signum],
270                 getpid());
271 }
272
273 // used to terminate child processes when program terminates
274 static void handle_exit(int signum)
275 {
276 //printf("child %d exit\n", getpid());
277         exit(0);
278 }
279
280 void BC_Signals::set_sighup_exit(int enable)
281 {
282         if( enable ) {
283 // causes SIGHUP to be generated when parent dies
284                 signal(SIGHUP, handle_exit);
285                 prctl(PR_SET_PDEATHSIG, SIGHUP, 0,0,0);
286 // prevents ^C from signalling child when attached to gdb
287                 setpgid(0, 0);
288                 if( isatty(0) ) ioctl(0, TIOCNOTTY, 0);
289         }
290         else {
291                 signal(SIGHUP, signal_entry);
292                 prctl(PR_SET_PDEATHSIG, 0,0,0,0);
293         }
294 }
295
296 BC_Signals::BC_Signals()
297 {
298 }
299 BC_Signals::~BC_Signals()
300 {
301   BC_CModels::bcxfer_stop_slicers();
302 }
303
304
305 int BC_Signals::x_error_handler(Display *display, XErrorEvent *event)
306 {
307         char string[1024];
308         XGetErrorText(event->display, event->error_code, string, 1024);
309         fprintf(stderr, "BC_Signals::x_error_handler: error_code=%d opcode=%d,%d id=0x%jx %s\n",
310                 event->error_code, event->request_code, event->minor_code,
311                 (int64_t)event->resourceid, string);
312         return 0;
313 }
314
315
316 void BC_Signals::initialize(const char *trap_path)
317 {
318         BC_Signals::global_signals = this;
319         BC_Trace::global_trace = this;
320         set_trap_path(trap_path);
321         handler_lock = (pthread_mutex_t*)calloc(1, sizeof(pthread_mutex_t));
322         pthread_mutex_init(handler_lock, 0);
323         old_err_handler = XSetErrorHandler(x_error_handler);
324         initialize2();
325 }
326
327 void BC_Signals::terminate()
328 {
329         BC_Signals::global_signals = 0;
330         BC_Trace::global_trace = 0;
331         uncatch_segv();  uncatch_intr();
332         signal(SIGHUP, SIG_DFL);
333         signal(SIGINT, SIG_DFL);
334         signal(SIGQUIT, SIG_DFL);
335         signal(SIGTERM, SIG_DFL);
336         signal(SIGFPE, SIG_DFL);
337         signal(SIGPIPE, SIG_DFL);
338         signal(SIGUSR2, SIG_DFL);
339         XSetErrorHandler(old_err_handler);
340 }
341
342 // kill SIGUSR2
343 void BC_Signals::signal_dump(int signum)
344 {
345         BC_KeyboardHandler::kill_grabs();
346         dump();
347         signal(SIGUSR2, signal_dump);
348 }
349
350
351
352
353
354
355 void BC_Signals::initialize2()
356 {
357         signal(SIGHUP, signal_entry);
358         signal(SIGINT, signal_entry);
359         signal(SIGQUIT, signal_entry);
360         // SIGKILL cannot be stopped
361         // signal(SIGKILL, signal_entry);
362         catch_segv();
363         signal(SIGTERM, signal_entry);
364         signal(SIGFPE, signal_entry);
365         signal(SIGPIPE, signal_entry_recoverable);
366         signal(SIGUSR2, signal_dump);
367 }
368
369
370 void BC_Signals::signal_handler(int signum)
371 {
372 printf("BC_Signals::signal_handler\n");
373 //      exit(0);
374 }
375
376 const char* BC_Signals::sig_to_str(int number)
377 {
378         return signal_titles[number];
379 }
380
381
382 #include <ucontext.h>
383 #include <sys/wait.h>
384 #include "thread.h"
385
386 #if __i386__
387 #define IP eip
388 #define sigregs_t sigcontext
389
390 static void reg_dump(FILE *fp,sigregs_t *rp)
391 {
392         fprintf(fp,"REGS:\n");
393         fprintf(fp,"  gs: %04x:%04x\n", rp->gs,rp->__gsh);
394         fprintf(fp,"  fs: %04x:%04x\n", rp->fs,rp->__fsh);
395         fprintf(fp,"  es: %04x:%04x\n", rp->es,rp->__esh);
396         fprintf(fp,"  ds: %04x:%04x\n", rp->ds,rp->__dsh);
397         fprintf(fp," edi: %14p %d\n", (void*)rp->edi,rp->edi);
398         fprintf(fp," esi: %14p %d\n", (void*)rp->esi,rp->esi);
399         fprintf(fp," ebp: %14p %d\n", (void*)rp->ebp,rp->ebp);
400         fprintf(fp," esp: %14p %d\n", (void*)rp->esp,rp->esp);
401         fprintf(fp," ebx: %14p %d\n", (void*)rp->ebx,rp->ebx);
402         fprintf(fp," edx: %14p %d\n", (void*)rp->edx,rp->edx);
403         fprintf(fp," ecx: %14p %d\n", (void*)rp->ecx,rp->ecx);
404         fprintf(fp," eax: %14p %d\n", (void*)rp->eax,rp->eax);
405         fprintf(fp," trapno: %14p %d\n", (void*)rp->trapno,rp->trapno);
406         fprintf(fp," err: %14p %d\n", (void*)rp->err,rp->err);
407         fprintf(fp," eip: %14p %d\n", (void*)rp->eip,rp->eip);
408         fprintf(fp," cs: %04xd : %04x\n", rp->cs,rp->__csh);
409         fprintf(fp," eflags: %14p %d\n", (void*)rp->eflags,rp->eflags);
410         fprintf(fp," esp_at_signal: %p %d\n", (void*)rp->esp_at_signal,rp->esp_at_signal);
411         fprintf(fp," ss: %04xd : %04x\n", rp->ss,rp->__ssh);
412         fprintf(fp," oldmask: %14p %d\n", (void*)rp->oldmask,rp->oldmask);
413         fprintf(fp," cr2: %14p %d\n", (void*)rp->cr2,rp->cr2);
414         fprintf(fp,"\n");
415 }
416 #endif
417
418 #if __x86_64__
419 #define IP rip
420 #define sigregs_t sigcontext
421
422 static void reg_dump(FILE *fp,sigregs_t *rp)
423 {
424         fprintf(fp,"REGS:\n");
425         fprintf(fp,"  r8: %20p %jd\n", (void*)rp->r8,rp->r8);
426         fprintf(fp,"  r9: %20p %jd\n", (void*)rp->r9,rp->r9);
427         fprintf(fp," r10: %20p %jd\n", (void*)rp->r10,rp->r10);
428         fprintf(fp," r11: %20p %jd\n", (void*)rp->r11,rp->r11);
429         fprintf(fp," r12: %20p %jd\n", (void*)rp->r12,rp->r12);
430         fprintf(fp," r13: %20p %jd\n", (void*)rp->r13,rp->r13);
431         fprintf(fp," r14: %20p %jd\n", (void*)rp->r14,rp->r14);
432         fprintf(fp," r15: %20p %jd\n", (void*)rp->r15,rp->r15);
433         fprintf(fp," rdi: %20p %jd\n", (void*)rp->rdi,rp->rdi);
434         fprintf(fp," rsi: %20p %jd\n", (void*)rp->rsi,rp->rsi);
435         fprintf(fp," rbp: %20p %jd\n", (void*)rp->rbp,rp->rbp);
436         fprintf(fp," rbx: %20p %jd\n", (void*)rp->rbx,rp->rbx);
437         fprintf(fp," rdx: %20p %jd\n", (void*)rp->rdx,rp->rdx);
438         fprintf(fp," rax: %20p %jd\n", (void*)rp->rax,rp->rax);
439         fprintf(fp," rcx: %20p %jd\n", (void*)rp->rcx,rp->rcx);
440         fprintf(fp," rsp: %20p %jd\n", (void*)rp->rsp,rp->rsp);
441         fprintf(fp," rip: %20p %jd\n", (void*)rp->rip,rp->rip);
442         fprintf(fp," eflags: %14p %jd\n", (void*)rp->eflags,rp->eflags);
443         fprintf(fp,"  cs: %04x\n", rp->cs);
444         fprintf(fp,"  gs: %04x\n", rp->gs);
445         fprintf(fp,"  fs: %04x\n", rp->fs);
446         fprintf(fp," err: %20p %jd\n", (void*)rp->err,rp->err);
447         fprintf(fp," trapno: %20p %jd\n", (void*)rp->trapno,rp->trapno);
448         fprintf(fp," oldmask: %20p %jd\n", (void*)rp->oldmask,rp->oldmask);
449         fprintf(fp," cr2: %20p %jd\n", (void*)rp->cr2,rp->cr2);
450         fprintf(fp,"\n");
451 }
452
453 #endif
454
455 #if __powerpc__ || __powerpc64__ || __powerpc64le__
456 #include <asm/ptrace.h>
457 #define IP nip
458 #define sigregs_t pt_regs
459 static void reg_dump(FILE *fp,sigregs_t *rp) {}
460 #endif
461
462 #ifndef IP
463 #error gotta have IP
464 #endif
465
466
467 static void handle_dump(int n, siginfo_t * info, void *sc)
468 {
469         uncatch_segv();  uncatch_intr();
470         signal(SIGSEGV, SIG_DFL);
471         signal(SIGINT, SIG_DFL);
472         // gotta be root, or the dump is worthless
473         int uid = getuid();
474 // it is not necessary to be root if ptrace is allowed via:
475 // echo 0 > /proc/sys/kernel/yama/ptrace_scope (usually set to 1)
476 //      if( uid != 0 ) return;
477         ucontext_t *uc = (ucontext_t *)sc;
478         int pid = getpid(), tid = gettid();
479         struct sigregs_t *c = (struct sigregs_t *)&uc->uc_mcontext;
480         uint8_t *ip = (uint8_t *)c->IP;
481         fprintf(stderr,"** %s at %p in pid %d, tid %d\n",
482                 n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
483                 (void*)ip, pid, tid);
484         FILE *fp = 0;
485         char fn[PATH_MAX];
486         if( BC_Signals::trap_path ) {
487                 snprintf(fn, sizeof(fn), BC_Signals::trap_path, pid);
488                 fp = fopen(fn,"w");
489         }
490         if( fp ) {
491                 fprintf(stderr,"writing debug data to %s\n", fn);
492                 fprintf(fp,"** %s at %p in pid %d, tid %d\n",
493                         n==SIGSEGV? "segv" : n==SIGINT? "intr" : "trap",
494                         (void*)c->IP, pid, tid);
495         }
496         else {
497                 strcpy(fn, "stdout");
498                 fp = stdout;
499         }
500         time_t t;  time(&t);
501         fprintf(fp,"created on %s", ctime(&t));
502         struct passwd *pw = getpwuid(uid);
503         if( pw ) {
504                 fprintf(fp,"        by %d:%d %s(%s)\n",
505                         pw->pw_uid, pw->pw_gid, pw->pw_name, pw->pw_gecos);
506         }
507         fprintf(fp,"\nCPUS: %d\n",   BC_Resources::get_machine_cpus());
508         fprintf(fp,"\nCPUINFO:\n");  bc_copy_textfile(32, fp,"/proc/cpuinfo");
509         fprintf(fp,"\nTHREADS:\n");  BC_Trace::dump_threads(fp);
510         fprintf(fp,"\nTRACES:\n");   BC_Trace::dump_traces(fp);
511         fprintf(fp,"\nLOCKS:\n");    BC_Trace::dump_locks(fp);
512         fprintf(fp,"\nBUFFERS:\n");  BC_Trace::dump_buffers(fp);
513         fprintf(fp,"\nSHMMEM:\n");   BC_Trace::dump_shm_stats(fp);
514         if( BC_Signals::trap_hook ) {
515                 fprintf(fp,"\nMAIN HOOK:\n");
516                 BC_Signals::trap_hook(fp, BC_Signals::trap_data);
517         }
518         fprintf(fp,"\nVERSION:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/version");
519         fprintf(fp,"\nMEMINFO:\n");  bc_copy_textfile(INT_MAX, fp,"/proc/meminfo");
520         fprintf(fp,"\nSTATUS:\n");   bc_copy_textfile(INT_MAX, fp,"/proc/%d/status",pid);
521         fprintf(fp,"\nFD:\n");       bc_list_openfiles(INT_MAX, fp,"/proc/%d/fd", pid);
522         fprintf(fp,"\nMAPS:\n");     bc_copy_textfile(INT_MAX, fp,"/proc/%d/maps",pid);
523         char proc_mem[64];
524         if( tid > 0 && tid != pid )
525                 sprintf(proc_mem,"/proc/%d/task/%d/mem",pid,tid);
526         else
527                 sprintf(proc_mem,"/proc/%d/mem",pid);
528         int pfd = open(proc_mem,O_RDONLY);
529         if( pfd >= 0 ) {
530                 fprintf(fp,"\nCODE:\n");
531                 for( int i=-32; i<32; ) {
532                         uint8_t v;  void *vp = (void *)(ip + i);
533                         if( !(i & 7) ) fprintf(fp,"%p:  ", vp);
534                         if( pread(pfd,&v,sizeof(v),(off_t)vp) != sizeof(v) ) break;
535                         fprintf(fp,"%c%02x", !i ? '>' : ' ', v);
536                         if( !(++i & 7) ) fprintf(fp,"\n");
537                 }
538                 fprintf(fp,"\n");
539                 close(pfd);
540         }
541         else
542                 fprintf(fp,"err opening: %s, %m\n", proc_mem);
543
544         reg_dump(fp, c);
545         fprintf(fp,"\n\n");
546         if( fp != stdout ) fclose(fp);
547         char cmd[1024], *cp = cmd;
548         cp += sprintf(cp, "exec gdb /proc/%d/exe -p %d --batch --quiet "
549                 "-ex \"thread apply all info registers\" "
550                 "-ex \"thread apply all bt full\" "
551                 "-ex \"quit\"", pid, pid);
552         if( fp != stdout )
553                 cp += sprintf(cp," >> \"%s\"", fn);
554         cp += sprintf(cp," 2>&1");
555 //printf("handle_dump:: pid=%d, cmd='%s'  fn='%s'\n",pid,cmd,fn);
556         pid = vfork();
557         if( pid < 0 ) {
558                 fprintf(stderr,"** can't start gdb, dump abondoned\n");
559                 return;
560         }
561         if( pid > 0 ) {
562                 waitpid(pid,0,0);
563                 fprintf(stderr,"** dump complete\n");
564                 return;
565         }
566         char *const argv[4] = { (char*) "/bin/sh", (char*) "-c", cmd, 0 };
567         execvp(argv[0], &argv[0]);
568 }
569