1308d2509Sflorian #include "config.h"
2308d2509Sflorian #include <assert.h>
3308d2509Sflorian #include <errno.h>
4308d2509Sflorian #include <fcntl.h>
5308d2509Sflorian #include <stdlib.h>
6308d2509Sflorian #include <unistd.h>
7308d2509Sflorian #include <string.h>
8308d2509Sflorian #include <errno.h>
9308d2509Sflorian #include <sys/time.h>
10308d2509Sflorian
11308d2509Sflorian #include "popen3.h"
12308d2509Sflorian
close_pipe(int fds[2])13308d2509Sflorian static void close_pipe(int fds[2])
14308d2509Sflorian {
15308d2509Sflorian if(fds[0] != -1) {
16308d2509Sflorian close(fds[0]);
17308d2509Sflorian fds[0] = -1;
18308d2509Sflorian }
19308d2509Sflorian if(fds[1] != -1) {
20308d2509Sflorian close(fds[1]);
21308d2509Sflorian fds[1] = -1;
22308d2509Sflorian }
23308d2509Sflorian }
24308d2509Sflorian
popen3(char * const * command,int * fdinptr,int * fdoutptr,int * fderrptr)25308d2509Sflorian pid_t popen3(char *const *command,
26*3f21e8ccSflorian int *fdinptr,
27*3f21e8ccSflorian int *fdoutptr,
28*3f21e8ccSflorian int *fderrptr)
29308d2509Sflorian {
30308d2509Sflorian int err = 0;
31308d2509Sflorian int fdin[] = { -1, -1 };
32308d2509Sflorian int fdout[] = { -1, -1 };
33308d2509Sflorian int fderr[] = { -1, -1 };
34308d2509Sflorian int fdsig[] = { -1, -1 };
35308d2509Sflorian pid_t pid;
36308d2509Sflorian ssize_t discard;
37308d2509Sflorian
38308d2509Sflorian if(command == NULL || *command == NULL) {
39308d2509Sflorian errno = EINVAL;
40308d2509Sflorian return -1;
41308d2509Sflorian }
42308d2509Sflorian
43*3f21e8ccSflorian if(fdinptr != NULL && pipe(fdin) == -1) {
44308d2509Sflorian goto error;
45308d2509Sflorian }
46*3f21e8ccSflorian if(fdoutptr != NULL && pipe(fdout) == -1) {
47308d2509Sflorian goto error;
48308d2509Sflorian }
49*3f21e8ccSflorian if(fderrptr != NULL && pipe(fderr) == -1) {
50308d2509Sflorian goto error;
51308d2509Sflorian }
52308d2509Sflorian if(pipe(fdsig) == -1 ||
53308d2509Sflorian fcntl(fdsig[0], F_SETFD, FD_CLOEXEC) == -1 ||
54308d2509Sflorian fcntl(fdsig[1], F_SETFD, FD_CLOEXEC) == -1)
55308d2509Sflorian {
56308d2509Sflorian goto error;
57308d2509Sflorian }
58308d2509Sflorian
59308d2509Sflorian pid = fork();
60308d2509Sflorian switch(pid) {
61308d2509Sflorian case -1: /* error */
62308d2509Sflorian goto error;
63308d2509Sflorian case 0: /* child */
64*3f21e8ccSflorian if(fderrptr != NULL) {
65308d2509Sflorian if(dup2(fderr[1], 2) == -1) {
66308d2509Sflorian goto error_dup2;
67308d2509Sflorian }
68308d2509Sflorian close_pipe(fderr);
69308d2509Sflorian } else {
70308d2509Sflorian close(2);
71308d2509Sflorian }
72*3f21e8ccSflorian if(fdoutptr != NULL) {
73308d2509Sflorian if(dup2(fdout[1], 1) == -1) {
74308d2509Sflorian goto error_dup2;
75308d2509Sflorian }
76308d2509Sflorian close_pipe(fdout);
77308d2509Sflorian } else {
78308d2509Sflorian close(1);
79308d2509Sflorian }
80*3f21e8ccSflorian if(fdinptr != NULL) {
81308d2509Sflorian if(dup2(fdin[0], 0) == -1) {
82308d2509Sflorian goto error_dup2;
83308d2509Sflorian }
84308d2509Sflorian close_pipe(fdin);
85308d2509Sflorian } else {
86308d2509Sflorian close(0);
87308d2509Sflorian }
88308d2509Sflorian
89308d2509Sflorian execvp(*command, command);
90308d2509Sflorian error_dup2:
91308d2509Sflorian err = errno;
92308d2509Sflorian close(fdsig[0]);
93308d2509Sflorian discard = write(fdsig[1], &err, sizeof(err));
94308d2509Sflorian (void)discard;
95308d2509Sflorian close(fdsig[1]);
96308d2509Sflorian exit(-1);
97308d2509Sflorian default: /* parent */
98308d2509Sflorian {
99308d2509Sflorian /* wait for signal pipe to close */
100308d2509Sflorian int ret;
101308d2509Sflorian fd_set rfds;
102308d2509Sflorian
103308d2509Sflorian close(fdsig[1]);
104308d2509Sflorian fdsig[1] = -1;
105308d2509Sflorian do {
106308d2509Sflorian FD_ZERO(&rfds);
107308d2509Sflorian FD_SET(fdsig[0], &rfds);
108308d2509Sflorian ret = select(fdsig[0] + 1, &rfds, NULL, NULL, NULL);
109308d2509Sflorian } while(ret == -1 && errno == EINTR);
110308d2509Sflorian
111308d2509Sflorian if(ret == -1) {
112308d2509Sflorian goto error;
113308d2509Sflorian }
114308d2509Sflorian
115308d2509Sflorian if((ret = read(fdsig[0], &err, sizeof(err))) != 0) {
116308d2509Sflorian if(ret != -1) {
117308d2509Sflorian assert(ret == sizeof(err));
118308d2509Sflorian errno = err;
119308d2509Sflorian }
120308d2509Sflorian goto error;
121308d2509Sflorian }
122308d2509Sflorian close(fdsig[0]);
123308d2509Sflorian fdsig[0] = -1;
124308d2509Sflorian }
125308d2509Sflorian break;
126308d2509Sflorian }
127308d2509Sflorian
128*3f21e8ccSflorian if(fdinptr != NULL) {
129308d2509Sflorian close(fdin[0]);
130*3f21e8ccSflorian *fdinptr = fdin[1];
131308d2509Sflorian }
132*3f21e8ccSflorian if(fdoutptr != NULL) {
133308d2509Sflorian close(fdout[1]);
134*3f21e8ccSflorian *fdoutptr = fdout[0];
135308d2509Sflorian }
136*3f21e8ccSflorian if(fderrptr != NULL) {
137308d2509Sflorian close(fderr[1]);
138*3f21e8ccSflorian *fderrptr = fderr[0];
139308d2509Sflorian }
140308d2509Sflorian
141308d2509Sflorian return pid;
142308d2509Sflorian
143308d2509Sflorian error:
144308d2509Sflorian err = errno;
145308d2509Sflorian
146308d2509Sflorian close_pipe(fdin);
147308d2509Sflorian close_pipe(fdout);
148308d2509Sflorian close_pipe(fderr);
149308d2509Sflorian close_pipe(fdsig);
150308d2509Sflorian
151308d2509Sflorian errno = err;
152308d2509Sflorian
153308d2509Sflorian return -1;
154308d2509Sflorian }
155