xref: /inferno-os/emu/Solaris/cmd.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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[3];	/* fd[0] is standard input, fd[1] is standard output, fd[2] is standard error */
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->fd[2] && i != t->wfd)
47  			close(i);
48  
49  	dup2(t->fd[0], 0);
50  	dup2(t->fd[1], 1);
51  	dup2(t->fd[2], 2);
52  	close(t->fd[0]);
53  	close(t->fd[1]);
54  	close(t->fd[2]);
55  
56  	/* should have an auth file to do host-specific authorisation? */
57  	if(t->gid != -1){
58  		if(setgid(t->gid) < 0 && getegid() == 0){
59  			fprint(t->wfd, "can't set gid %d: %s", t->gid, strerror(errno));
60  			_exit(1);
61  		}
62  	}
63  
64  	if(t->uid != -1){
65  		if(setuid(t->uid) < 0 && geteuid() == 0){
66  			fprint(t->wfd, "can't set uid %d: %s", t->uid, strerror(errno));
67  			_exit(1);
68  		}
69  	}
70  
71  	if(t->dir != nil && chdir(t->dir) < 0){
72  		fprint(t->wfd, "can't chdir to %s: %s", t->dir, strerror(errno));
73  		_exit(1);
74  	}
75  
76  	signal(SIGPIPE, SIG_DFL);
77  
78  	execvp(t->args[0], t->args);
79  	if(Debug)
80  		print("execvp: %s\n",strerror(errno));
81  	fprint(t->wfd, "exec failed: %s", strerror(errno));
82  
83  	_exit(1);
84  }
85  
86  void*
87  oscmd(char **args, int nice, char *dir, int *fd)
88  {
89  	Targ *t;
90  	int r, fd0[2], fd1[2], fd2[2], wfd[2], n, pid;
91  
92  	t = mallocz(sizeof(*t), 1);
93  	if(t == nil)
94  		return nil;
95  
96  	fd0[0] = fd0[1] = -1;
97  	fd1[0] = fd1[1] = -1;
98  	fd2[0] = fd2[1] = -1;
99  	wfd[0] = wfd[1] = -1;
100  	if(pipe(fd0) < 0 || pipe(fd1) < 0 || pipe(fd2) < 0 || pipe(wfd) < 0)
101  		goto Error;
102  	if(fcntl(wfd[1], F_SETFD, FD_CLOEXEC) < 0)	/* close on exec to give end of file on success */
103  		goto Error;
104  
105  	t->fd[0] = fd0[0];
106  	t->fd[1] = fd1[1];
107  	t->fd[2] = fd2[1];
108  	t->wfd = wfd[1];
109  	t->args = args;
110  	t->dir = dir;
111  	t->gid = up->env->gid;
112  	if(t->gid == -1)
113  		t->gid = gidnobody;
114  	t->uid = up->env->uid;
115  	if(t->uid == -1)
116  		t->uid = uidnobody;
117  
118  	signal(SIGCHLD, SIG_DFL);
119  	switch(pid = fork()) {
120  	case -1:
121  		goto Error;
122  	case 0:
123  		setpgrp();
124  		if(nice)
125  			oslopri();
126  		childproc(t);
127  		_exit(1);
128  	default:
129  		t->pid = pid;
130  		if(Debug)
131  			print("cmd pid %d\n", t->pid);
132  		break;
133  	}
134  
135  	close(fd0[0]);
136  	close(fd1[1]);
137  	close(fd2[1]);
138  	close(wfd[1]);
139  
140  	n = read(wfd[0], up->genbuf, sizeof(up->genbuf)-1);
141  	close(wfd[0]);
142  	if(n > 0){
143  		close(fd0[1]);
144  		close(fd1[0]);
145  		close(fd2[0]);
146  		free(t);
147  		up->genbuf[n] = 0;
148  		if(Debug)
149  			print("oscmd: bad exec: %q\n", up->genbuf);
150  		error(up->genbuf);
151  		return nil;
152  	}
153  
154  	fd[0] = fd0[1];
155  	fd[1] = fd1[0];
156  	fd[2] = fd2[0];
157  	return t;
158  
159  Error:
160  	r = errno;
161  	if(Debug)
162  		print("oscmd: %q\n",strerror(r));
163  	close(fd0[0]);
164  	close(fd0[1]);
165  	close(fd1[0]);
166  	close(fd1[1]);
167  	close(fd2[0]);
168  	close(fd2[1]);
169  	close(wfd[0]);
170  	close(wfd[1]);
171  	error(strerror(r));
172  	return nil;
173  }
174  
175  int
176  oscmdkill(void *a)
177  {
178  	Targ *t = a;
179  
180  	if(Debug)
181  		print("kill: %d\n", t->pid);
182  	return kill(-t->pid, SIGTERM);
183  }
184  
185  int
186  oscmdwait(void *a, char *buf, int n)
187  {
188  	Targ *t = a;
189  	int s;
190  
191  	if(waitpid(t->pid, &s, 0) == -1){
192  		if(Debug)
193  			print("wait error: %d [in %d] %q\n", t->pid, getpid(), strerror(errno));
194  		return -1;
195  	}
196  	if(WIFEXITED(s)){
197  		if(WEXITSTATUS(s) == 0)
198  			return snprint(buf, n, "%d 0 0 0 ''", t->pid);
199  		return snprint(buf, n, "%d 0 0 0 'exit: %d'", t->pid, WEXITSTATUS(s));
200  	}
201  	if(WIFSIGNALED(s)){
202  		if(WTERMSIG(s) == SIGTERM || WTERMSIG(s) == SIGKILL)
203  			return snprint(buf, n, "%d 0 0 0 killed", t->pid);
204  		return snprint(buf, n, "%d 0 0 0 'signal: %d'", t->pid, WTERMSIG(s));
205  	}
206  	return snprint(buf, n, "%d 0 0 0 'odd status: 0x%x'", t->pid, s);
207  }
208  
209  void
210  oscmdfree(void *a)
211  {
212  	free(a);
213  }
214