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];
14 _threaddebug(DBGEXEC, "threadexec %s", prog);
16 /*
17 * We want threadexec to behave like exec; if exec succeeds,
18 * never return, and if it fails, return with errstr set.
19 * Unfortunately, the exec happens in another proc since
20 * we have to wait for the exec'ed process to finish.
21 * To provide the semantics, we open a pipe with the
22 * write end close-on-exec and hand it to the proc that
23 * is doing the exec. If the exec succeeds, the pipe will
24 * close so that our read below fails. If the exec fails,
25 * then the proc doing the exec sends the errstr down the
26 * pipe to us.
27 */
28 if(pipe(pfd) < 0)
29 goto Bad;
30 if(fcntl(pfd[0], F_SETFD, 1) < 0)
31 goto Bad;
32 if(fcntl(pfd[1], F_SETFD, 1) < 0)
33 goto Bad;
35 switch(pid = fork()){
36 case -1:
37 close(pfd[0]);
38 close(pfd[1]);
39 goto Bad;
40 case 0:
41 efork(fd, pfd, prog, args);
42 _exit(0);
43 default:
44 if(freeargs)
45 free(args);
46 break;
47 }
49 close(pfd[1]);
50 if((n = read(pfd[0], exitstr, ERRMAX-1)) > 0){ /* exec failed */
51 exitstr[n] = '\0';
52 errstr(exitstr, ERRMAX);
53 close(pfd[0]);
54 goto Bad;
55 }
56 close(pfd[0]);
57 close(fd[0]);
58 if(fd[1] != fd[0])
59 close(fd[1]);
60 if(fd[2] != fd[1] && fd[2] != fd[0])
61 close(fd[2]);
62 if(pidc)
63 sendul(pidc, pid);
65 _threaddebug(DBGEXEC, "threadexec schedexecwait");
66 return pid;
68 Bad:
69 _threaddebug(DBGEXEC, "threadexec bad %r");
70 if(pidc)
71 sendul(pidc, ~0);
72 return -1;
73 }
75 void
76 threadexec(Channel *pidc, int fd[3], char *prog, char *args[])
77 {
78 if(_threadexec(pidc, fd, prog, args, 0) >= 0)
79 threadexits(nil);
80 }
82 int
83 threadspawn(int fd[3], char *prog, char *args[])
84 {
85 return _threadexec(nil, fd, prog, args, 0);
86 }
88 /*
89 * The &f+1 trick doesn't work on SunOS, so we might
90 * as well bite the bullet and do this correctly.
91 */
92 void
93 threadexecl(Channel *pidc, int fd[3], char *f, ...)
94 {
95 char **args, *s;
96 int n;
97 va_list arg;
99 va_start(arg, f);
100 for(n=0; va_arg(arg, char*) != 0; n++)
102 n++;
103 va_end(arg);
105 args = malloc(n*sizeof(args[0]));
106 if(args == nil){
107 if(pidc)
108 sendul(pidc, ~0);
109 return;
112 va_start(arg, f);
113 for(n=0; (s=va_arg(arg, char*)) != 0; n++)
114 args[n] = s;
115 args[n] = 0;
116 va_end(arg);
118 if(_threadexec(pidc, fd, f, args, 1) >= 0)
119 threadexits(nil);
122 static void
123 efork(int stdfd[3], int fd[2], char *prog, char **args)
125 char buf[ERRMAX];
126 int i;
128 _threaddebug(DBGEXEC, "_schedexec %s -- calling execv", prog);
129 dup(stdfd[0], 0);
130 dup(stdfd[1], 1);
131 dup(stdfd[2], 2);
132 for(i=3; i<40; i++)
133 if(i != fd[1])
134 close(i);
135 rfork(RFNOTEG);
136 execvp(prog, args);
137 _threaddebug(DBGEXEC, "_schedexec failed: %r");
138 rerrstr(buf, sizeof buf);
139 if(buf[0]=='\0')
140 strcpy(buf, "exec failed");
141 write(fd[1], buf, strlen(buf));
142 close(fd[1]);
143 _exits(buf);