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