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