xref: /openbsd-src/usr.sbin/nsd/popen3.c (revision 3f21e8cc40aa89506971c1c4154dcba359c18fc3)
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