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