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