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