| #include "u.h" |
| #include <errno.h> |
| #include "libc.h" |
| #include "thread.h" |
| #include "threadimpl.h" |
| |
| static Lock thewaitlock; |
| static Channel *thewaitchan; |
| static Channel *dowaitchan; |
| |
| /* BUG - start waitproc on first exec, not when threadwaitchan is called */ |
| static void |
| waitproc(void *v) |
| { |
| Channel *c; |
| Waitmsg *w; |
| |
| _threadsetsysproc(); |
| for(;;){ |
| while((w = wait()) == nil){ |
| if(errno == ECHILD) |
| recvul(dowaitchan); |
| } |
| if((c = thewaitchan) != nil) |
| sendp(c, w); |
| else |
| free(w); |
| } |
| } |
| |
| Channel* |
| threadwaitchan(void) |
| { |
| if(thewaitchan) |
| return thewaitchan; |
| lock(&thewaitlock); |
| if(thewaitchan){ |
| unlock(&thewaitlock); |
| return thewaitchan; |
| } |
| thewaitchan = chancreate(sizeof(Waitmsg*), 4); |
| chansetname(thewaitchan, "threadwaitchan"); |
| dowaitchan = chancreate(sizeof(ulong), 1); |
| chansetname(dowaitchan, "dowaitchan"); |
| proccreate(waitproc, nil, STACK); |
| unlock(&thewaitlock); |
| return thewaitchan; |
| } |
| |
| int |
| threadspawn(int fd[3], char *cmd, char *argv[]) |
| { |
| int i, n, p[2], pid; |
| char exitstr[100]; |
| |
| if(pipe(p) < 0) |
| return -1; |
| if(fcntl(p[0], F_SETFD, 1) < 0 || fcntl(p[1], F_SETFD, 1) < 0){ |
| close(p[0]); |
| close(p[1]); |
| return -1; |
| } |
| switch(pid = fork()){ |
| case -1: |
| close(p[0]); |
| close(p[1]); |
| return -1; |
| case 0: |
| dup2(fd[0], 0); |
| dup2(fd[1], 1); |
| dup2(fd[2], 2); |
| for(i=3; i<100; i++) |
| if(i != p[1]) |
| close(i); |
| execvp(cmd, argv); |
| fprint(p[1], "%d", errno); |
| close(p[1]); |
| _exit(0); |
| } |
| |
| close(p[1]); |
| n = read(p[0], exitstr, sizeof exitstr-1); |
| close(p[0]); |
| if(n > 0){ /* exec failed */ |
| exitstr[n] = 0; |
| errno = atoi(exitstr); |
| return -1; |
| } |
| |
| close(fd[0]); |
| if(fd[1] != fd[0]) |
| close(fd[1]); |
| if(fd[2] != fd[1] && fd[2] != fd[0]) |
| close(fd[2]); |
| channbsendul(dowaitchan, 1); |
| return pid; |
| } |
| |
| int |
| _threadexec(Channel *cpid, int fd[3], char *cmd, char *argv[]) |
| { |
| int pid; |
| |
| pid = threadspawn(fd, cmd, argv); |
| if(cpid){ |
| if(pid < 0) |
| chansendul(cpid, ~0); |
| else |
| chansendul(cpid, pid); |
| } |
| return pid; |
| } |
| |
| void |
| threadexec(Channel *cpid, int fd[3], char *cmd, char *argv[]) |
| { |
| if(_threadexec(cpid, fd, cmd, argv) >= 0) |
| threadexits("threadexec"); |
| } |
| |
| void |
| threadexecl(Channel *cpid, int fd[3], char *cmd, ...) |
| { |
| char **argv, *s; |
| int n, pid; |
| va_list arg; |
| |
| va_start(arg, cmd); |
| for(n=0; va_arg(arg, char*) != nil; n++) |
| ; |
| n++; |
| va_end(arg); |
| |
| argv = malloc(n*sizeof(argv[0])); |
| if(argv == nil){ |
| if(cpid) |
| chansendul(cpid, ~0); |
| return; |
| } |
| |
| va_start(arg, cmd); |
| for(n=0; (s=va_arg(arg, char*)) != nil; n++) |
| argv[n] = s; |
| argv[n] = 0; |
| va_end(arg); |
| |
| pid = _threadexec(cpid, fd, cmd, argv); |
| free(argv); |
| |
| if(pid >= 0) |
| threadexits("threadexecl"); |
| } |
| |