rfork.c (2824B)
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 22 if((flags&(RFPROC|RFFDG|RFMEM)) == (RFPROC|RFFDG)){ 23 /* check other flags before we commit */ 24 flags &= ~(RFPROC|RFFDG|RFENVG); 25 n = (flags & ~(RFNOTEG|RFNAMEG|RFNOWAIT|RFCENVG)); 26 if(n){ 27 werrstr("unknown flags %08ux in rfork", n); 28 return -1; 29 } 30 if(flags&RFNOWAIT){ 31 /* 32 * BUG - should put the signal handler back after we 33 * finish, but I just don't care. If a program calls with 34 * NOWAIT once, they're not likely to want child notes 35 * after that. 36 */ 37 signal(SIGCHLD, nop); 38 if(pipe(p) < 0) 39 return -1; 40 } 41 pid = fork(); 42 if(pid == -1) 43 return -1; 44 if(flags&RFNOWAIT){ 45 flags &= ~RFNOWAIT; 46 if(pid){ 47 /* 48 * Parent - wait for child to fork wait-free child. 49 * Then read pid from pipe. Assume pipe buffer can absorb the write. 50 */ 51 close(p[1]); 52 status = 0; 53 if(wait4(pid, &status, 0, 0) < 0){ 54 werrstr("pipe dance - wait4 - %r"); 55 close(p[0]); 56 return -1; 57 } 58 n = readn(p[0], buf, sizeof buf-1); 59 close(p[0]); 60 if(!WIFEXITED(status) || WEXITSTATUS(status)!=0 || n <= 0){ 61 if(!WIFEXITED(status)) 62 werrstr("pipe dance - !exited 0x%ux", status); 63 else if(WEXITSTATUS(status) != 0) 64 werrstr("pipe dance - non-zero status 0x%ux", status); 65 else if(n < 0) 66 werrstr("pipe dance - pipe read error - %r"); 67 else if(n == 0) 68 werrstr("pipe dance - pipe read eof"); 69 else 70 werrstr("pipe dance - unknown failure"); 71 return -1; 72 } 73 buf[n] = 0; 74 if(buf[0] == 'x'){ 75 werrstr("%s", buf+2); 76 return -1; 77 } 78 pid = strtol(buf, &q, 0); 79 }else{ 80 /* 81 * Child - fork a new child whose wait message can't 82 * get back to the parent because we're going to exit! 83 */ 84 signal(SIGCHLD, SIG_IGN); 85 close(p[0]); 86 pid = fork(); 87 if(pid){ 88 /* Child parent - send status over pipe and exit. */ 89 if(pid > 0) 90 fprint(p[1], "%d", pid); 91 else 92 fprint(p[1], "x %r"); 93 close(p[1]); 94 _exit(0); 95 }else{ 96 /* Child child - close pipe. */ 97 close(p[1]); 98 } 99 } 100 } 101 if(pid != 0) 102 return pid; 103 if(flags&RFCENVG) 104 if(environ) 105 *environ = nil; 106 } 107 if(flags&RFPROC){ 108 werrstr("cannot use rfork for shared memory -- use libthread"); 109 return -1; 110 } 111 if(flags&RFNAMEG){ 112 /* XXX set $NAMESPACE to a new directory */ 113 flags &= ~RFNAMEG; 114 } 115 if(flags&RFNOTEG){ 116 setpgid(0, getpid()); 117 flags &= ~RFNOTEG; 118 } 119 if(flags&RFNOWAIT){ 120 werrstr("cannot use RFNOWAIT without RFPROC"); 121 return -1; 122 } 123 if(flags){ 124 werrstr("unknown flags %08ux in rfork", flags); 125 return -1; 126 } 127 return 0; 128 }