123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- #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;
- 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){
- /*
- * BUG - should put the signal handler back after we
- * finish, but I just don't care. If a program calls with
- * NOWAIT once, they're not likely to want child notes
- * after that.
- */
- 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]);
- }
- }
- }
- 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;
- }
|