9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

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 }