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
close_pipe(int fds[2])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
popen3(char * const * command,int * fdinptr,int * fdoutptr,int * fderrptr)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