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 int *fdinptr, 27 int *fdoutptr, 28 int *fderrptr) 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 pid_t pid; 36 ssize_t discard; 37 38 if(command == NULL || *command == NULL) { 39 errno = EINVAL; 40 return -1; 41 } 42 43 if(fdinptr != NULL && pipe(fdin) == -1) { 44 goto error; 45 } 46 if(fdoutptr != NULL && pipe(fdout) == -1) { 47 goto error; 48 } 49 if(fderrptr != NULL && pipe(fderr) == -1) { 50 goto error; 51 } 52 if(pipe(fdsig) == -1 || 53 fcntl(fdsig[0], F_SETFD, FD_CLOEXEC) == -1 || 54 fcntl(fdsig[1], F_SETFD, FD_CLOEXEC) == -1) 55 { 56 goto error; 57 } 58 59 pid = fork(); 60 switch(pid) { 61 case -1: /* error */ 62 goto error; 63 case 0: /* child */ 64 if(fderrptr != NULL) { 65 if(dup2(fderr[1], 2) == -1) { 66 goto error_dup2; 67 } 68 close_pipe(fderr); 69 } else { 70 close(2); 71 } 72 if(fdoutptr != NULL) { 73 if(dup2(fdout[1], 1) == -1) { 74 goto error_dup2; 75 } 76 close_pipe(fdout); 77 } else { 78 close(1); 79 } 80 if(fdinptr != NULL) { 81 if(dup2(fdin[0], 0) == -1) { 82 goto error_dup2; 83 } 84 close_pipe(fdin); 85 } else { 86 close(0); 87 } 88 89 execvp(*command, command); 90 error_dup2: 91 err = errno; 92 close(fdsig[0]); 93 discard = write(fdsig[1], &err, sizeof(err)); 94 (void)discard; 95 close(fdsig[1]); 96 exit(-1); 97 default: /* parent */ 98 { 99 /* wait for signal pipe to close */ 100 int ret; 101 fd_set rfds; 102 103 close(fdsig[1]); 104 fdsig[1] = -1; 105 do { 106 FD_ZERO(&rfds); 107 FD_SET(fdsig[0], &rfds); 108 ret = select(fdsig[0] + 1, &rfds, NULL, NULL, NULL); 109 } while(ret == -1 && errno == EINTR); 110 111 if(ret == -1) { 112 goto error; 113 } 114 115 if((ret = read(fdsig[0], &err, sizeof(err))) != 0) { 116 if(ret != -1) { 117 assert(ret == sizeof(err)); 118 errno = err; 119 } 120 goto error; 121 } 122 close(fdsig[0]); 123 fdsig[0] = -1; 124 } 125 break; 126 } 127 128 if(fdinptr != NULL) { 129 close(fdin[0]); 130 *fdinptr = fdin[1]; 131 } 132 if(fdoutptr != NULL) { 133 close(fdout[1]); 134 *fdoutptr = fdout[0]; 135 } 136 if(fderrptr != NULL) { 137 close(fderr[1]); 138 *fderrptr = fderr[0]; 139 } 140 141 return pid; 142 143 error: 144 err = errno; 145 146 close_pipe(fdin); 147 close_pipe(fdout); 148 close_pipe(fderr); 149 close_pipe(fdsig); 150 151 errno = err; 152 153 return -1; 154 } 155