xref: /minix3/minix/tests/test67.c (revision 3083d603ba9dea68befc9da05a714884e10ec7e8)
1 #include <arpa/inet.h>
2 #include <netinet/in.h>
3 #include <sys/socket.h>
4 #include <sys/types.h>
5 #include <sys/wait.h>
6 #include <sys/syslimits.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <netdb.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 
14 int max_error = 5;
15 #include "common.h"
16 
17 #define CLOEXEC_PORT 3490
18 #define FORK_PORT 3491
19 
20 static int fd = 0;
21 
22 void copy_subtests(void);
23 void test_open_file_cloexec(void);
24 void test_open_file_fork(void);
25 void test_open_socket_cloexec(void);
26 void test_open_socket_fork(void);
27 void start_socket_server(int port);
28 int start_socket_client(int port, int flag);
29 
30 void
copy_subtests()31 copy_subtests()
32 {
33 	char *subtests[] = { "t67a", "t67b" };
34 	char copy_cmd[8 + PATH_MAX + 1];
35 	int i, no_tests;
36 
37 	no_tests = sizeof(subtests) / sizeof(char *);
38 
39 	for (i = 0; i < no_tests; i++) {
40 		snprintf(copy_cmd, 8 + PATH_MAX, "cp ../%s .", subtests[i]);
41 		system(copy_cmd);
42 	}
43 }
44 
45 void
test_open_file_cloexec()46 test_open_file_cloexec()
47 {
48 	int flags;
49 	pid_t pid;
50 
51 	/* Let's create a file with O_CLOEXEC turned on */
52 	fd = open("file", O_RDWR|O_CREAT|O_CLOEXEC, 0660);
53 	if (fd < 0) e(1);
54 
55 	/* Now verify through fcntl the flag is indeed set */
56 	flags = fcntl(fd, F_GETFD);
57 	if (flags < 0) e(2);
58 	if (!(flags & FD_CLOEXEC)) e(3);
59 
60 	/* Fork a child and let child exec a test program that verifies
61 	 * fd is not a valid file */
62 	pid = fork();
63 	if (pid == -1) e(4);
64 	else if (pid == 0) {
65 		/* We're the child */
66 		char fd_buf[2];
67 
68 		/* Verify again O_CLOEXEC is on */
69 		flags = fcntl(fd, F_GETFD);
70 		if (flags < 0) e(5);
71 		if (!(flags & FD_CLOEXEC)) e(6);
72 
73 		snprintf(fd_buf, sizeof(fd_buf), "%d", fd);
74 		execl("./t67b", "t67b", fd_buf, NULL);
75 
76 		/* Should not reach this */
77 		exit(1);
78 	} else {
79 		/* We're the parent */
80 		int result;
81 
82 		if (waitpid(pid, &result, 0) == -1) e(7);
83 		if (WEXITSTATUS(result) != 0) e(8);
84 	}
85 	close(fd);
86 }
87 
88 void
test_open_file_fork()89 test_open_file_fork()
90 {
91 	int flags;
92 	pid_t pid;
93 
94 	/* Let's create a file with O_CLOEXEC NOT turned on */
95 	fd = open("file", O_RDWR|O_CREAT, 0660);
96 	if (fd < 0) e(1);
97 
98 	/* Now verify through fcntl the flag is indeed not set */
99 	flags = fcntl(fd, F_GETFD);
100 	if (flags < 0) e(2);
101 	if (flags & FD_CLOEXEC) e(3);
102 
103 	/* Fork a child and let child exec a test program that verifies
104 	 * fd is a valid file */
105 	pid = fork();
106 	if (pid == -1) e(4);
107 	else if (pid == 0) {
108 		/* We're the child */
109 		char fd_buf[2];
110 
111 		/* Verify again O_CLOEXEC is off */
112 		flags = fcntl(fd, F_GETFD);
113 		if (flags < 0) e(5);
114 		if (flags & FD_CLOEXEC) e(6);
115 
116 		snprintf(fd_buf, sizeof(fd_buf), "%d", fd);
117 		execl("./t67a", "t67a", fd_buf, NULL);
118 
119 		/* Should not reach this */
120 		exit(1);
121 	} else {
122 		/* We're the parent */
123 		int result = 0;
124 
125 		if (waitpid(pid, &result, 0) == -1) e(7);
126 		if (WEXITSTATUS(result) != 0) e(8);
127 	}
128 	close(fd);
129 }
130 
131 int
start_socket_client(int port,int flag)132 start_socket_client(int port, int flag)
133 {
134 	int fd_sock;
135 	struct hostent *he;
136 	struct sockaddr_in server;
137 
138 	if ((fd_sock = socket(PF_INET, SOCK_STREAM|flag, 0)) < 0) {
139 		perror("Error obtaining socket\n");
140 		e(1);
141 	}
142 
143 	if ((he = gethostbyname("127.0.0.1")) == NULL) {
144 		perror("Error retrieving home\n");
145 		e(2);
146 	}
147 
148 	/* Copy server host result */
149 	memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
150 	server.sin_family = AF_INET;
151 	server.sin_port = htons(port);
152 
153 	/* Normally, we'd zerofill sin_zero, but there is no such thing on
154 	 * Minix at the moment */
155 #if !defined(__minix)
156 	memset(&server.sin_zero, '\0', sizeof(server.sin_zero));
157 #endif
158 
159 	if (connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0){
160 		perror("Error connecting to server\n");
161 		e(3);
162 	}
163 
164 	return fd_sock;
165 }
166 
167 
168 void
start_socket_server(int port)169 start_socket_server(int port)
170 {
171 #if !defined(__minix)
172 	int yes = 1;
173 #endif
174 	int fd_sock, fd_new;
175 	struct sockaddr_in my_addr;
176 	struct sockaddr_in other_addr;
177 	socklen_t other_size;
178 	char buf[1];
179 
180 	if ((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
181 		perror("Error getting socket\n");
182 		e(1);
183 	}
184 
185 	my_addr.sin_family = AF_INET;
186 	my_addr.sin_port = htons(port);
187 	my_addr.sin_addr.s_addr = INADDR_ANY;
188 	/* Normally we'd zerofill sin_zero, but current Minix socket interface
189 	 * does not support that field */
190 #if !defined(__minix)
191 	memset(&my_addr.sin_zero, '\0', sizeof(sin.sin_zero));
192 #endif
193 
194 	/* Reuse port number when invoking test often */
195 #if !defined(__minix)
196 	if (setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes,
197 	    sizeof(int)) < 0) {
198 		perror("Error setting port reuse option\n");
199 		e(2);
200 	}
201 #endif
202 
203 	/* Bind to port */
204 	if (bind(fd_sock, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
205 		perror("Error binding to port\n");
206 		e(3);
207 	}
208 
209 	/* Set socket in listening mode */
210 	if (listen(fd_sock, 20) < 0) {
211 		perror("Error listening for incoming connections");
212 		e(4);
213 	}
214 
215 	/* Accept incoming connections */
216 	fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
217 
218 	if (fd_new < 0) {
219 		perror("Error accepting new connections\n");
220 		e(5);
221 	}
222 
223 	(void)read(fd_new, buf, sizeof(buf));
224 	exit(0);
225 }
226 
227 void
test_open_socket_cloexec()228 test_open_socket_cloexec()
229 {
230 /* This subtest will start a server and client using TCP. The client will
231  * open the socket with SOCK_CLOEXEC turned on, so that after a fork+exec, the
232  * socket should become invalid.
233  *                o
234  *              / |
235  *        server  |
236  *      (accept)  |
237  *             |  | \
238  *             |  |  client
239  *             |  |  (connect)
240  *             |  |  | \
241  *             |  |  |  client_fork
242  *             |  |  |  (exec t67b)
243  *        (read)  |  |  (write)
244  *             |  |  | /
245  *             |  |  (waitpid client_fork)
246  *              \ |  |
247  * (waitpid server)  |
248  *                | /
249  * (waitpid client)
250  *                |
251  *                o
252  */
253 	pid_t pid_server, pid_client;
254 	int result;
255 
256 	pid_server = fork();
257 	if (pid_server < 0) e(1);
258 	if (pid_server == 0) {
259 		start_socket_server(CLOEXEC_PORT);
260 		return; /* Never reached */
261 	}
262 
263 	pid_client = fork();
264 	if (pid_client < 0) e(2);
265 	if (pid_client == 0) {
266 		pid_t pid_client_fork;
267 		int sockfd;
268 
269 		sockfd = start_socket_client(CLOEXEC_PORT, SOCK_CLOEXEC);
270 		if (sockfd < 0) e(4);
271 
272 		pid_client_fork = fork();
273 		if (pid_client_fork < 0) {
274 			e(5);
275 			exit(5);
276 		}
277 		if (pid_client_fork == 0) {
278 			/* We're a fork of the client. After we exec, the
279 			 * socket should become invalid due to the SOCK_CLOEXEC
280 			 * flag.
281 			 */
282 			char sockfd_buf[2];
283 			int flags;
284 
285 			/* Verify O_CLOEXEC is on */
286 			flags = fcntl(sockfd, F_GETFD);
287 			if (flags < 0) e(5);
288 			if (!(flags & FD_CLOEXEC)) e(6);
289 
290 			/* t67b will verify that it can't write to sockfd and
291 			 * that opening a new file will yield a file descriptor
292 			 * with the same number.
293 			 */
294 			snprintf(sockfd_buf, sizeof(sockfd_buf), "%d", sockfd);
295 			execl("./t67b", "t67b", sockfd_buf, NULL);
296 
297 			/* Should not reach this */
298 			exit(1);
299 		} else {
300 			if (waitpid(pid_client_fork, &result, 0) < 0) e(8);
301 			exit(WEXITSTATUS(result)); /* Pass on error to main */
302 		}
303 		exit(0);	/* Never reached */
304 	}
305 
306 	if (waitpid(pid_server, &result, 0) < 0) e(3);
307 	if (waitpid(pid_client, &result, 0) < 0) e(4);
308 
309 	/* Let's inspect client result */
310 	if (WEXITSTATUS(result) != 0) e(5);
311 }
312 
313 void
test_open_socket_fork(void)314 test_open_socket_fork(void)
315 {
316 /* This subtest will start a server and client using TCP. The client will
317  * open the socket with SOCK_CLOEXEC turned off, so that after a fork+exec, the
318  * socket should stay valid.
319  *                o
320  *              / |
321  *        server  |
322  *      (accept)  |
323  *             |  | \
324  *             |  |  client
325  *             |  |  (connect)
326  *             |  |  | \
327  *             |  |  |  client_fork
328  *             |  |  |  (exec t67a)
329  *        (read)  |  |  (write)
330  *             |  |  | /
331  *             |  |  (waitpid client_fork)
332  *              \ |  |
333  * (waitpid server)  |
334  *                | /
335  * (waitpid client)
336  *                |
337  *                o
338  */
339 	pid_t pid_server, pid_client;
340 	int result;
341 
342 	pid_server = fork();
343 	if (pid_server < 0) e(1);
344 	if (pid_server == 0) {
345 		start_socket_server(FORK_PORT);
346 		return; /* Never reached */
347 	}
348 
349 	pid_client = fork();
350 	if (pid_client < 0) e(2);
351 	if (pid_client == 0) {
352 		pid_t pid_client_fork;
353 		int sockfd;
354 
355 		sockfd = start_socket_client(FORK_PORT, 0);
356 		if (sockfd < 0) e(4);
357 
358 		pid_client_fork = fork();
359 		if (pid_client_fork < 0) {
360 			e(5);
361 			exit(5);
362 		}
363 		if (pid_client_fork == 0) {
364 			/* We're a fork of the client. After we exec, the
365 			 * socket should stay valid due to lack of SOCK_CLOEXEC
366 			 * flag.
367 			 */
368 			char sockfd_buf[2];
369 			int flags;
370 
371 			/* Verify O_CLOEXEC is off */
372 			flags = fcntl(sockfd, F_GETFD);
373 			if (flags < 0) e(5);
374 			if (flags & FD_CLOEXEC) e(6);
375 
376 			/* t67a will verify that it can't write to sockfd and
377 			 * that opening a new file will yield a file descriptor
378 			 * with a higher number.
379 			 */
380 			snprintf(sockfd_buf, sizeof(sockfd_buf), "%d", sockfd);
381 			execl("./t67a", "t67a", sockfd_buf, NULL);
382 
383 			/* Should not reach this */
384 			exit(1);
385 		} else {
386 			if (waitpid(pid_client_fork, &result, 0) < 0) e(8);
387 			exit(WEXITSTATUS(result)); /* Pass on error to main */
388 		}
389 		exit(0);	/* Never reached */
390 	}
391 
392 	if (waitpid(pid_server, &result, 0) < 0) e(3);
393 	if (waitpid(pid_client, &result, 0) < 0) e(4);
394 
395 	/* Let's inspect client result */
396 	if (WEXITSTATUS(result) != 0) e(5);
397 }
398 
399 int
main(int argc,char * argv[])400 main(int argc, char *argv[])
401 {
402 	start(67);
403 	copy_subtests();
404 	test_open_file_fork();
405 	test_open_file_cloexec();
406 	test_open_socket_fork();
407 	test_open_socket_cloexec();
408 	quit();
409 	return(-1);	/* Unreachable */
410 }
411 
412