|  | #include <u.h> | 
|  | #include <sys/wait.h> | 
|  | #include <signal.h> | 
|  | #include <libc.h> | 
|  | #undef rfork | 
|  |  | 
|  | static void | 
|  | nop(int x) | 
|  | { | 
|  | USED(x); | 
|  | } | 
|  |  | 
|  | int | 
|  | p9rfork(int flags) | 
|  | { | 
|  | int pid, status; | 
|  | int p[2]; | 
|  | int n; | 
|  | char buf[128], *q; | 
|  | extern char **environ; | 
|  | struct sigaction oldchld; | 
|  |  | 
|  | memset(&oldchld, 0, sizeof oldchld); | 
|  |  | 
|  | if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){ | 
|  | /* check other flags before we commit */ | 
|  | flags &= ~(RFPROC|RFFDG|RFENVG); | 
|  | n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT|RFCENVG)); | 
|  | if(n){ | 
|  | werrstr("unknown flags %08ux in rfork", n); | 
|  | return -1; | 
|  | } | 
|  | if(flags&RFNOWAIT){ | 
|  | sigaction(SIGCHLD, nil, &oldchld); | 
|  | signal(SIGCHLD, nop); | 
|  | if(pipe(p) < 0) | 
|  | return -1; | 
|  | } | 
|  | pid = fork(); | 
|  | if(pid == -1) | 
|  | return -1; | 
|  | if(flags&RFNOWAIT){ | 
|  | flags &= ~RFNOWAIT; | 
|  | if(pid){ | 
|  | /* | 
|  | * Parent - wait for child to fork wait-free child. | 
|  | * Then read pid from pipe.  Assume pipe buffer can absorb the write. | 
|  | */ | 
|  | close(p[1]); | 
|  | status = 0; | 
|  | if(wait4(pid, &status, 0, 0) < 0){ | 
|  | werrstr("pipe dance - wait4 - %r"); | 
|  | close(p[0]); | 
|  | return -1; | 
|  | } | 
|  | n = readn(p[0], buf, sizeof buf-1); | 
|  | close(p[0]); | 
|  | if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){ | 
|  | if(!WIFEXITED(status)) | 
|  | werrstr("pipe dance - !exited 0x%ux", status); | 
|  | else if(WEXITSTATUS(status) != 0) | 
|  | werrstr("pipe dance - non-zero status 0x%ux", status); | 
|  | else if(n < 0) | 
|  | werrstr("pipe dance - pipe read error - %r"); | 
|  | else if(n == 0) | 
|  | werrstr("pipe dance - pipe read eof"); | 
|  | else | 
|  | werrstr("pipe dance - unknown failure"); | 
|  | return -1; | 
|  | } | 
|  | buf[n] = 0; | 
|  | if(buf[0] == 'x'){ | 
|  | werrstr("%s", buf+2); | 
|  | return -1; | 
|  | } | 
|  | pid = strtol(buf, &q, 0); | 
|  | }else{ | 
|  | /* | 
|  | * Child - fork a new child whose wait message can't | 
|  | * get back to the parent because we're going to exit! | 
|  | */ | 
|  | signal(SIGCHLD, SIG_IGN); | 
|  | close(p[0]); | 
|  | pid = fork(); | 
|  | if(pid){ | 
|  | /* Child parent - send status over pipe and exit. */ | 
|  | if(pid > 0) | 
|  | fprint(p[1], "%d", pid); | 
|  | else | 
|  | fprint(p[1], "x %r"); | 
|  | close(p[1]); | 
|  | _exit(0); | 
|  | }else{ | 
|  | /* Child child - close pipe. */ | 
|  | close(p[1]); | 
|  | } | 
|  | } | 
|  | sigaction(SIGCHLD, &oldchld, nil); | 
|  | } | 
|  | if(pid != 0) | 
|  | return pid; | 
|  | if(flags&RFCENVG) | 
|  | if(environ) | 
|  | *environ = nil; | 
|  | } | 
|  | if(flags&RFPROC){ | 
|  | werrstr("cannot use rfork for shared memory -- use libthread"); | 
|  | return -1; | 
|  | } | 
|  | if(flags&RFNAMEG){ | 
|  | /* XXX set $NAMESPACE to a new directory */ | 
|  | flags &= ~RFNAMEG; | 
|  | } | 
|  | if(flags&RFNOTEG){ | 
|  | setpgid(0, getpid()); | 
|  | flags &= ~RFNOTEG; | 
|  | } | 
|  | if(flags&RFNOWAIT){ | 
|  | werrstr("cannot use RFNOWAIT without RFPROC"); | 
|  | return -1; | 
|  | } | 
|  | if(flags){ | 
|  | werrstr("unknown flags %08ux in rfork", flags); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } |