1 #include "threadimpl.h"
2 
3 static Lock thewaitlock;
4 static Channel *thewaitchan;
5 
6 static void
execproc(void * v)7 execproc(void *v)
8 {
9 	int pid;
10 	Channel *c;
11 	Execjob *e;
12 	Waitmsg *w;
13 
14 	e = v;
15 	pid = _threadspawn(e->fd, e->cmd, e->argv, e->dir);
16 	sendul(e->c, pid);
17 	if(pid > 0){
18 		w = waitfor(pid);
19 		if((c = thewaitchan) != nil)
20 			sendp(c, w);
21 		else
22 			free(w);
23 	}
24 	threadexits(nil);
25 }
26 
27 int
_runthreadspawn(int * fd,char * cmd,char ** argv,char * dir)28 _runthreadspawn(int *fd, char *cmd, char **argv, char *dir)
29 {
30 	int pid;
31 	Execjob e;
32 
33 	e.fd = fd;
34 	e.cmd = cmd;
35 	e.argv = argv;
36 	e.dir = dir;
37 	e.c = chancreate(sizeof(void*), 0);
38 	proccreate(execproc, &e, 65536);
39 	pid = recvul(e.c);
40 	chanfree(e.c);
41 	return pid;
42 }
43 
44 Channel*
threadwaitchan(void)45 threadwaitchan(void)
46 {
47 	if(thewaitchan)
48 		return thewaitchan;
49 	lock(&thewaitlock);
50 	if(thewaitchan){
51 		unlock(&thewaitlock);
52 		return thewaitchan;
53 	}
54 	thewaitchan = chancreate(sizeof(Waitmsg*), 4);
55 	chansetname(thewaitchan, "threadwaitchan");
56 	unlock(&thewaitlock);
57 	return thewaitchan;
58 }
59 
60 int
_threadspawn(int fd[3],char * cmd,char * argv[],char * dir)61 _threadspawn(int fd[3], char *cmd, char *argv[], char *dir)
62 {
63 	int i, n, p[2], pid;
64 	char exitstr[100];
65 
66 	notifyoff("sys: child");	/* do not let child note kill us */
67 	if(pipe(p) < 0)
68 		return -1;
69 	if(fcntl(p[0], F_SETFD, 1) < 0 || fcntl(p[1], F_SETFD, 1) < 0){
70 		close(p[0]);
71 		close(p[1]);
72 		return -1;
73 	}
74 	switch(pid = fork()){
75 	case -1:
76 		close(p[0]);
77 		close(p[1]);
78 		return -1;
79 	case 0:
80 		/* can't RFNOTEG - will lose tty */
81 		if(dir != nil )
82 			chdir(dir); /* best effort */
83 		dup2(fd[0], 0);
84 		dup2(fd[1], 1);
85 		dup2(fd[2], 2);
86 		if(!isatty(0) && !isatty(1) && !isatty(2))
87 			rfork(RFNOTEG);
88 		for(i=3; i<100; i++)
89 			if(i != p[1])
90 				close(i);
91 		execvp(cmd, argv);
92 		fprint(p[1], "%d", errno);
93 		close(p[1]);
94 		_exit(0);
95 	}
96 
97 	close(p[1]);
98 	n = read(p[0], exitstr, sizeof exitstr-1);
99 	close(p[0]);
100 	if(n > 0){	/* exec failed */
101 		free(waitfor(pid));
102 		exitstr[n] = 0;
103 		errno = atoi(exitstr);
104 		return -1;
105 	}
106 
107 	close(fd[0]);
108 	if(fd[1] != fd[0])
109 		close(fd[1]);
110 	if(fd[2] != fd[1] && fd[2] != fd[0])
111 		close(fd[2]);
112 	return pid;
113 }
114 
115 int
threadspawn(int fd[3],char * cmd,char * argv[])116 threadspawn(int fd[3], char *cmd, char *argv[])
117 {
118 	return _runthreadspawn(fd, cmd, argv, nil);
119 }
120 
121 int
threadspawnd(int fd[3],char * cmd,char * argv[],char * dir)122 threadspawnd(int fd[3], char *cmd, char *argv[], char *dir)
123 {
124 	return _runthreadspawn(fd, cmd, argv, dir);
125 }
126 
127 int
threadspawnl(int fd[3],char * cmd,...)128 threadspawnl(int fd[3], char *cmd, ...)
129 {
130 	char **argv, *s;
131 	int n, pid;
132 	va_list arg;
133 
134 	va_start(arg, cmd);
135 	for(n=0; va_arg(arg, char*) != nil; n++)
136 		;
137 	n++;
138 	va_end(arg);
139 
140 	argv = malloc(n*sizeof(argv[0]));
141 	if(argv == nil)
142 		return -1;
143 
144 	va_start(arg, cmd);
145 	for(n=0; (s=va_arg(arg, char*)) != nil; n++)
146 		argv[n] = s;
147 	argv[n] = 0;
148 	va_end(arg);
149 
150 	pid = threadspawn(fd, cmd, argv);
151 	free(argv);
152 	return pid;
153 }
154 
155 int
_threadexec(Channel * cpid,int fd[3],char * cmd,char * argv[])156 _threadexec(Channel *cpid, int fd[3], char *cmd, char *argv[])
157 {
158 	int pid;
159 
160 	pid = threadspawn(fd, cmd, argv);
161 	if(cpid){
162 		if(pid < 0)
163 			chansendul(cpid, ~0);
164 		else
165 			chansendul(cpid, pid);
166 	}
167 	return pid;
168 }
169 
170 void
threadexec(Channel * cpid,int fd[3],char * cmd,char * argv[])171 threadexec(Channel *cpid, int fd[3], char *cmd, char *argv[])
172 {
173 	if(_threadexec(cpid, fd, cmd, argv) >= 0)
174 		threadexits("threadexec");
175 }
176 
177 void
threadexecl(Channel * cpid,int fd[3],char * cmd,...)178 threadexecl(Channel *cpid, int fd[3], char *cmd, ...)
179 {
180 	char **argv, *s;
181 	int n, pid;
182 	va_list arg;
183 
184 	va_start(arg, cmd);
185 	for(n=0; va_arg(arg, char*) != nil; n++)
186 		;
187 	n++;
188 	va_end(arg);
189 
190 	argv = malloc(n*sizeof(argv[0]));
191 	if(argv == nil){
192 		if(cpid)
193 			chansendul(cpid, ~0);
194 		return;
195 	}
196 
197 	va_start(arg, cmd);
198 	for(n=0; (s=va_arg(arg, char*)) != nil; n++)
199 		argv[n] = s;
200 	argv[n] = 0;
201 	va_end(arg);
202 
203 	pid = _threadexec(cpid, fd, cmd, argv);
204 	free(argv);
205 
206 	if(pid >= 0)
207 		threadexits("threadexecl");
208 }
209