xref: /plan9-contrib/sys/src/libthread/exec.c (revision e7469f7cc458260881920c645908657cf1c4e7b7)
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "threadimpl.h"
5 
6 #define PIPEMNT	"/mnt/temp"
7 
8 void
procexec(Channel * pidc,char * prog,char * args[])9 procexec(Channel *pidc, char *prog, char *args[])
10 {
11 	int n;
12 	Proc *p;
13 	Thread *t;
14 
15 	_threaddebug(DBGEXEC, "procexec %s", prog);
16 	/* must be only thread in proc */
17 	p = _threadgetproc();
18 	t = p->thread;
19 	if(p->threads.head != t || p->threads.head->nextt != nil){
20 		werrstr("not only thread in proc");
21 	Bad:
22 		if(pidc)
23 			sendul(pidc, ~0);
24 		return;
25 	}
26 
27 	/*
28 	 * We want procexec 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(bind("#|", PIPEMNT, MREPL) < 0)
40 		goto Bad;
41 	if((p->exec.fd[0] = open(PIPEMNT "/data", OREAD)) < 0){
42 		unmount(nil, PIPEMNT);
43 		goto Bad;
44 	}
45 	if((p->exec.fd[1] = open(PIPEMNT "/data1", OWRITE|OCEXEC)) < 0){
46 		close(p->exec.fd[0]);
47 		unmount(nil, PIPEMNT);
48 		goto Bad;
49 	}
50 	unmount(nil, PIPEMNT);
51 
52 	/* exec in parallel via the scheduler */
53 	assert(p->needexec==0);
54 	p->exec.prog = prog;
55 	p->exec.args = args;
56 	p->needexec = 1;
57 	_sched();
58 
59 	close(p->exec.fd[1]);
60 	if((n = read(p->exec.fd[0], p->exitstr, ERRMAX-1)) > 0){	/* exec failed */
61 		p->exitstr[n] = '\0';
62 		errstr(p->exitstr, ERRMAX);
63 		close(p->exec.fd[0]);
64 		goto Bad;
65 	}
66 	close(p->exec.fd[0]);
67 
68 	if(pidc)
69 		sendul(pidc, t->ret);
70 
71 	/* wait for exec'ed program, then exit */
72 	_schedexecwait();
73 }
74 
75 void
procexecl(Channel * pidc,char * f,...)76 procexecl(Channel *pidc, char *f, ...)
77 {
78 	procexec(pidc, f, &f+1);
79 }
80 
81 static void
execproc(void * v)82 execproc(void *v)
83 {
84 	Execjob *e;
85 
86 	e = v;
87 	rfork(RFFDG);
88 	dup(e->fd[0], 0);
89 	dup(e->fd[1], 1);
90 	dup(e->fd[2], 2);
91 	procexec(e->c, e->cmd, e->argv);
92 	threadexits(nil);
93 }
94 
95 int
threadspawn(int fd[3],char * cmd,char * argv[])96 threadspawn(int fd[3], char *cmd, char *argv[])
97 {
98 	int pid;
99 	Execjob e;
100 
101 	e.fd = fd;
102 	e.cmd = cmd;
103 	e.argv = argv;
104 	e.c = chancreate(sizeof(void*), 0);
105 	proccreate(execproc, &e, 65536);
106 	close(fd[0]);
107 	close(fd[1]);
108 	close(fd[2]);
109 	pid = recvul(e.c);
110 	chanfree(e.c);
111 	return pid;
112 }
113 
114 int
threadspawnl(int fd[3],char * cmd,...)115 threadspawnl(int fd[3], char *cmd, ...)
116 {
117 	char **argv, *s;
118 	int n, pid;
119 	va_list arg;
120 
121 	va_start(arg, cmd);
122 	for(n=0; va_arg(arg, char*) != nil; n++)
123 		;
124 	n++;
125 	va_end(arg);
126 
127 	argv = malloc(n*sizeof(argv[0]));
128 	if(argv == nil)
129 		return -1;
130 
131 	va_start(arg, cmd);
132 	for(n=0; (s=va_arg(arg, char*)) != nil; n++)
133 		argv[n] = s;
134 	argv[n] = 0;
135 	va_end(arg);
136 
137 	pid = threadspawn(fd, cmd, argv);
138 	free(argv);
139 	return pid;
140 }
141