xref: /inferno-os/emu/MacOSX/cmd.c (revision 9dc22068e29604f4b484e746112a9a4efe6fd57f)
1 #include	<sys/types.h>
2 #include	<signal.h>
3 #include 	<pwd.h>
4 #include	<sys/time.h>
5 #include	<sys/resource.h>
6 #include	<sys/wait.h>
7 #include	<fcntl.h>
8 
9 #include	"dat.h"
10 #include	"fns.h"
11 #include	"error.h"
12 
13 enum
14 {
15 	Debug = 0
16 };
17 
18 /*
19  * os-specific devcmd support.
20  * this version should be reasonably portable across Unix systems.
21  */
22 typedef struct Targ Targ;
23 struct Targ
24 {
25 	int	fd[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
26 	char**	args;
27 	char*	dir;
28 	int	pid;
29 	int	wfd;	/* child writes errors that occur after the fork or on exec */
30 	int	uid;
31 	int	gid;
32 };
33 
34 extern int gidnobody;
35 extern int uidnobody;
36 
37 static int
38 childproc(Targ *t)
39 {
40 	int i, nfd;
41 
42 	if(Debug)
43 		print("devcmd: '%s'", t->args[0]);
44 
45 	nfd = getdtablesize();
46 	for(i = 0; i < nfd; i++)
47 		if(i != t->fd[0] && i != t->fd[1] && i != t->fd[2] && i != t->wfd)
48 			close(i);
49 
50 	dup2(t->fd[0], 0);
51 	dup2(t->fd[1], 1);
52 	dup2(t->fd[2], 2);
53 	close(t->fd[0]);
54 	close(t->fd[1]);
55 	close(t->fd[2]);
56 
57 	/* should have an auth file to do host-specific authorisation? */
58 	if(t->gid != -1){
59 		if(setgid(t->gid) < 0 && getegid() == 0){
60 			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
61 			_exit(1);
62 		}
63 	}
64 
65 	if(t->uid != -1){
66 		if(setuid(t->uid) < 0 && geteuid() == 0){
67 			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
68 			_exit(1);
69 		}
70 	}
71 
72 	if(t->dir != nil && chdir(t->dir) < 0){
73 		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
74 		_exit(1);
75 	}
76 
77 	signal(SIGPIPE, SIG_DFL);
78 
79 	execvp(t->args[0], t->args);
80 	if(Debug)
81 		print("execvp: %s\n",strerror(errno));
82 	fprint(t->wfd, "exec failed: %s", strerror(errno));
83 
84 	_exit(1);
85 }
86 
87 void*
88 oscmd(char **args, int nice, char *dir, int *fd)
89 {
90 	Targ *t;
91 	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
92 
93 	t = mallocz(sizeof(*t), 1);
94 	if(t == nil)
95 		return nil;
96 
97 	fd0[0] = fd0[1] = -1;
98 	fd1[0] = fd1[1] = -1;
99 	fd2[0] = fd2[1] = -1;
100 	wfd[0] = wfd[1] = -1;
101 	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
102 		goto Error;
103 	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
104 		goto Error;
105 
106 	t->fd[0] = fd0[0];
107 	t->fd[1] = fd1[1];
108 	t->fd[2] = fd2[1];
109 	t->wfd = wfd[1];
110 	t->args = args;
111 	t->dir = dir;
112 	t->gid = up->env->gid;
113 	if(t->gid == -1)
114 		t->gid = gidnobody;
115 	t->uid = up->env->uid;
116 	if(t->uid == -1)
117 		t->uid = uidnobody;
118 
119 	signal(SIGCHLD, SIG_DFL);
120 	switch(pid = fork()) {
121 	case -1:
122 		goto Error;
123 	case 0:
124 		setpgid(0, getpid());
125 		if(nice)
126 			oslopri();
127 		childproc(t);
128 		_exit(1);
129 	default:
130 		t->pid = pid;
131 		if(Debug)
132 			print("cmd pid %d\n", t->pid);
133 		break;
134 	}
135 
136 	close(fd0[0]);
137 	close(fd1[1]);
138 	close(fd2[1]);
139 	close(wfd[1]);
140 
141 	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
142 	close(wfd[0]);
143 	if(n > 0){
144 		close(fd0[1]);
145 		close(fd1[0]);
146 		close(fd2[0]);
147 		free(t);
148 		up->genbuf[n] = 0;
149 		if(Debug)
150 			print("oscmd: bad exec: %q\n", up->genbuf);
151 		error(up->genbuf);
152 		return nil;
153 	}
154 
155 	fd[0] = fd0[1];
156 	fd[1] = fd1[0];
157 	fd[2] = fd2[0];
158 	return t;
159 
160 Error:
161 	r = errno;
162 	if(Debug)
163 		print("oscmd: %q\n",strerror(r));
164 	close(fd0[0]);
165 	close(fd0[1]);
166 	close(fd1[0]);
167 	close(fd1[1]);
168 	close(fd2[0]);
169 	close(fd2[1]);
170 	close(wfd[0]);
171 	close(wfd[1]);
172 	error(strerror(r));
173 	return nil;
174 }
175 
176 int
177 oscmdkill(void *a)
178 {
179 	Targ *t = a;
180 
181 	if(Debug)
182 		print("kill: %d\n", t->pid);
183 	return kill(-t->pid, SIGTERM);
184 }
185 
186 int
187 oscmdwait(void *a, char *buf, int n)
188 {
189 	Targ *t = a;
190 	int s;
191 
192 	if(waitpid(t->pid, &s, 0) == -1){
193 		if(Debug)
194 			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
195 		return -1;
196 	}
197 	if(WIFEXITED(s)){
198 		if(WEXITSTATUS(s) == 0)
199 			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
200 		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
201 	}
202 	if(WIFSIGNALED(s)){
203 		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
204 			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
205 		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
206 	}
207 	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
208 }
209 
210 void
211 oscmdfree(void *a)
212 {
213 	free(a);
214 }
215