rfork.c (2758B)
1 #include <u.h> 2 #include <sys/wait.h> 3 #include <signal.h> 4 #include <libc.h> 5 #undef rfork 6 7 static void 8 nop(int x) 9 { 10 USED(x); 11 } 12 13 int 14 p9rfork(int flags) 15 { 16 int pid, status; 17 int p[2]; 18 int n; 19 char buf[128], *q; 20 extern char **environ; 21 struct sigaction oldchld; 22 23 memset(&oldchld, 0, sizeof oldchld); 24 25 if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){ 26 /* check other flags before we commit */ 27 flags &= ~(RFPROC|RFFDG|RFENVG); 28 n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT|RFCENVG)); 29 if(n){ 30 werrstr("unknown flags %08ux in rfork", n); 31 return -1; 32 } 33 if(flags&RFNOWAIT){ 34 sigaction(SIGCHLD, nil, &oldchld); 35 signal(SIGCHLD, nop); 36 if(pipe(p) < 0) 37 return -1; 38 } 39 pid = fork(); 40 if(pid == -1) 41 return -1; 42 if(flags&RFNOWAIT){ 43 flags &= ~RFNOWAIT; 44 if(pid){ 45 /* 46 * Parent - wait for child to fork wait-free child. 47 * Then read pid from pipe. Assume pipe buffer can absorb the write. 48 */ 49 close(p[1]); 50 status = 0; 51 if(wait4(pid, &status, 0, 0) < 0){ 52 werrstr("pipe dance - wait4 - %r"); 53 close(p[0]); 54 return -1; 55 } 56 n = readn(p[0], buf, sizeof buf-1); 57 close(p[0]); 58 if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){ 59 if(!WIFEXITED(status)) 60 werrstr("pipe dance - !exited 0x%ux", status); 61 else if(WEXITSTATUS(status) != 0) 62 werrstr("pipe dance - non-zero status 0x%ux", status); 63 else if(n < 0) 64 werrstr("pipe dance - pipe read error - %r"); 65 else if(n == 0) 66 werrstr("pipe dance - pipe read eof"); 67 else 68 werrstr("pipe dance - unknown failure"); 69 return -1; 70 } 71 buf[n] = 0; 72 if(buf[0] == 'x'){ 73 werrstr("%s", buf+2); 74 return -1; 75 } 76 pid = strtol(buf, &q, 0); 77 }else{ 78 /* 79 * Child - fork a new child whose wait message can't 80 * get back to the parent because we're going to exit! 81 */ 82 signal(SIGCHLD, SIG_IGN); 83 close(p[0]); 84 pid = fork(); 85 if(pid){ 86 /* Child parent - send status over pipe and exit. */ 87 if(pid > 0) 88 fprint(p[1], "%d", pid); 89 else 90 fprint(p[1], "x %r"); 91 close(p[1]); 92 _exit(0); 93 }else{ 94 /* Child child - close pipe. */ 95 close(p[1]); 96 } 97 } 98 sigaction(SIGCHLD, &oldchld, nil); 99 } 100 if(pid != 0) 101 return pid; 102 if(flags&RFCENVG) 103 if(environ) 104 *environ = nil; 105 } 106 if(flags&RFPROC){ 107 werrstr("cannot use rfork for shared memory -- use libthread"); 108 return -1; 109 } 110 if(flags&RFNAMEG){ 111 /* XXX set $NAMESPACE to a new directory */ 112 flags &= ~RFNAMEG; 113 } 114 if(flags&RFNOTEG){ 115 setpgid(0, getpid()); 116 flags &= ~RFNOTEG; 117 } 118 if(flags&RFNOWAIT){ 119 werrstr("cannot use RFNOWAIT without RFPROC"); 120 return -1; 121 } 122 if(flags){ 123 werrstr("unknown flags %08ux in rfork", flags); 124 return -1; 125 } 126 return 0; 127 }