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
childproc(Targ * t)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*
oscmd(char ** args,int nice,char * dir,int * fd)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
oscmdkill(void * a)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
oscmdwait(void * a,char * buf,int n)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
oscmdfree(void * a)211 oscmdfree(void *a)
212 {
213 free(a);
214 }
215