Blob


1 #include <u.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include "threadimpl.h"
6 static void efork(int[3], int[2], char*, char**);
7 static int
8 _threadexec(Channel *pidc, int fd[3], char *prog, char *args[], int freeargs)
9 {
10 int pfd[2];
11 int n, pid;
12 char exitstr[ERRMAX];
13 static int firstexec = 1;
14 static Lock lk;
16 _threaddebug(DBGEXEC, "threadexec %s", prog);
18 if(firstexec){
19 lock(&lk);
20 if(firstexec){
21 firstexec = 0;
22 _threadfirstexec();
23 }
24 unlock(&lk);
25 }
27 /*
28 * We want threadexec to behave like exec; if exec succeeds,
29 * never return, and if it fails, return with errstr set.
30 * Unfortunately, the exec happens in another proc since
31 * we have to wait for the exec'ed process to finish.
32 * To provide the semantics, we open a pipe with the
33 * write end close-on-exec and hand it to the proc that
34 * is doing the exec. If the exec succeeds, the pipe will
35 * close so that our read below fails. If the exec fails,
36 * then the proc doing the exec sends the errstr down the
37 * pipe to us.
38 */
39 if(pipe(pfd) < 0)
40 goto Bad;
41 if(fcntl(pfd[0], F_SETFD, 1) < 0)
42 goto Bad;
43 if(fcntl(pfd[1], F_SETFD, 1) < 0)
44 goto Bad;
46 switch(pid = fork()){
47 case -1:
48 close(pfd[0]);
49 close(pfd[1]);
50 goto Bad;
51 case 0:
52 efork(fd, pfd, prog, args);
53 _exit(0);
54 default:
55 _threadafterexec();
56 if(freeargs)
57 free(args);
58 break;
59 }
61 close(pfd[1]);
62 if((n = read(pfd[0], exitstr, ERRMAX-1)) > 0){ /* exec failed */
63 exitstr[n] = '\0';
64 errstr(exitstr, ERRMAX);
65 close(pfd[0]);
66 goto Bad;
67 }
68 close(pfd[0]);
69 close(fd[0]);
70 if(fd[1] != fd[0])
71 close(fd[1]);
72 if(fd[2] != fd[1] && fd[2] != fd[0])
73 close(fd[2]);
74 if(pidc)
75 sendul(pidc, pid);
77 _threaddebug(DBGEXEC, "threadexec schedexecwait");
78 return pid;
80 Bad:
81 _threaddebug(DBGEXEC, "threadexec bad %r");
82 if(pidc)
83 sendul(pidc, ~0);
84 return -1;
85 }
87 void
88 threadexec(Channel *pidc, int fd[3], char *prog, char *args[])
89 {
90 if(_threadexec(pidc, fd, prog, args, 0) >= 0)
91 threadexits(nil);
92 }
94 int
95 threadspawn(int fd[3], char *prog, char *args[])
96 {
97 return _threadexec(nil, fd, prog, args, 0);
98 }
100 /*
101 * The &f+1 trick doesn't work on SunOS, so we might
102 * as well bite the bullet and do this correctly.
103 */
104 void
105 threadexecl(Channel *pidc, int fd[3], char *f, ...)
107 char **args, *s;
108 int n;
109 va_list arg;
111 va_start(arg, f);
112 for(n=0; va_arg(arg, char*) != 0; n++)
114 n++;
115 va_end(arg);
117 args = malloc(n*sizeof(args[0]));
118 if(args == nil){
119 if(pidc)
120 sendul(pidc, ~0);
121 return;
124 va_start(arg, f);
125 for(n=0; (s=va_arg(arg, char*)) != 0; n++)
126 args[n] = s;
127 args[n] = 0;
128 va_end(arg);
130 if(_threadexec(pidc, fd, f, args, 1) >= 0)
131 threadexits(nil);
134 static void
135 efork(int stdfd[3], int fd[2], char *prog, char **args)
137 char buf[ERRMAX];
138 int i;
140 _threaddebug(DBGEXEC, "_schedexec %s -- calling execv", prog);
141 dup(stdfd[0], 0);
142 dup(stdfd[1], 1);
143 dup(stdfd[2], 2);
144 for(i=3; i<40; i++)
145 if(i != fd[1])
146 close(i);
147 rfork(RFNOTEG);
148 execvp(prog, args);
149 _threaddebug(DBGEXEC, "_schedexec failed: %r");
150 rerrstr(buf, sizeof buf);
151 if(buf[0]=='\0')
152 strcpy(buf, "exec failed");
153 write(fd[1], buf, strlen(buf));
154 close(fd[1]);
155 _exits(buf);