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