1 #include "config.h" 2 #include <assert.h> 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <stdlib.h> 6 #include <unistd.h> 7 #include <string.h> 8 #include <errno.h> 9 #include <sys/time.h> 10 11 #include "popen3.h" 12 13 static void close_pipe(int fds[2]) 14 { 15 if(fds[0] != -1) { 16 close(fds[0]); 17 fds[0] = -1; 18 } 19 if(fds[1] != -1) { 20 close(fds[1]); 21 fds[1] = -1; 22 } 23 } 24 25 pid_t popen3(char *const *command, 26 FILE **finptr, 27 FILE **foutptr, 28 FILE **ferrptr) 29 { 30 int err = 0; 31 int fdin[] = { -1, -1 }; 32 int fdout[] = { -1, -1 }; 33 int fderr[] = { -1, -1 }; 34 int fdsig[] = { -1, -1 }; 35 FILE *fin, *fout, *ferr; 36 pid_t pid; 37 ssize_t discard; 38 39 if(command == NULL || *command == NULL) { 40 errno = EINVAL; 41 return -1; 42 } 43 44 fin = fout = ferr = NULL; 45 46 if(finptr != NULL && (pipe(fdin) == -1 || 47 (fin = fdopen(fdin[1], "w")) == NULL)) 48 { 49 goto error; 50 } 51 if(foutptr != NULL && (pipe(fdout) == -1 || 52 (fout = fdopen(fdout[0], "r")) == NULL)) 53 { 54 goto error; 55 } 56 if(ferrptr != NULL && (pipe(fderr) == -1 || 57 (ferr = fdopen(fderr[0], "r")) == NULL)) 58 { 59 goto error; 60 } 61 if(pipe(fdsig) == -1 || 62 fcntl(fdsig[0], F_SETFD, FD_CLOEXEC) == -1 || 63 fcntl(fdsig[1], F_SETFD, FD_CLOEXEC) == -1) 64 { 65 goto error; 66 } 67 68 pid = fork(); 69 switch(pid) { 70 case -1: /* error */ 71 goto error; 72 case 0: /* child */ 73 if(ferrptr != NULL) { 74 if(dup2(fderr[1], 2) == -1) { 75 goto error_dup2; 76 } 77 close_pipe(fderr); 78 } else { 79 close(2); 80 } 81 if(foutptr != NULL) { 82 if(dup2(fdout[1], 1) == -1) { 83 goto error_dup2; 84 } 85 close_pipe(fdout); 86 } else { 87 close(1); 88 } 89 if(finptr != NULL) { 90 if(dup2(fdin[0], 0) == -1) { 91 goto error_dup2; 92 } 93 close_pipe(fdin); 94 } else { 95 close(0); 96 } 97 98 execvp(*command, command); 99 error_dup2: 100 err = errno; 101 close(fdsig[0]); 102 discard = write(fdsig[1], &err, sizeof(err)); 103 (void)discard; 104 close(fdsig[1]); 105 exit(-1); 106 default: /* parent */ 107 { 108 /* wait for signal pipe to close */ 109 int ret; 110 fd_set rfds; 111 112 close(fdsig[1]); 113 fdsig[1] = -1; 114 do { 115 FD_ZERO(&rfds); 116 FD_SET(fdsig[0], &rfds); 117 ret = select(fdsig[0] + 1, &rfds, NULL, NULL, NULL); 118 } while(ret == -1 && errno == EINTR); 119 120 if(ret == -1) { 121 goto error; 122 } 123 124 if((ret = read(fdsig[0], &err, sizeof(err))) != 0) { 125 if(ret != -1) { 126 assert(ret == sizeof(err)); 127 errno = err; 128 } 129 goto error; 130 } 131 close(fdsig[0]); 132 fdsig[0] = -1; 133 } 134 break; 135 } 136 137 if(finptr != NULL) { 138 close(fdin[0]); 139 *finptr = fin; 140 } 141 if(foutptr != NULL) { 142 close(fdout[1]); 143 *foutptr = fout; 144 } 145 if(ferrptr != NULL) { 146 close(fderr[1]); 147 *ferrptr = ferr; 148 } 149 150 return pid; 151 152 error: 153 err = errno; 154 155 if(fin != NULL) { 156 fclose(fin); 157 fdin[1] = -1; 158 } 159 if(fout != NULL) { 160 fclose(fout); 161 fdout[0] = -1; 162 } 163 if(ferr != NULL) { 164 fclose(ferr); 165 fderr[0] = -1; 166 } 167 168 close_pipe(fdin); 169 close_pipe(fdout); 170 close_pipe(fderr); 171 close_pipe(fdsig); 172 173 errno = err; 174 175 return -1; 176 } 177