1 /*	$OpenBSD: select_iocond.c,v 1.2 2021/12/08 13:22:53 visa Exp $	*/
2 
3 /*
4  * Copyright (c) 2021 Visa Hankala
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Test select(2) with various I/O conditions.
21  */
22 
23 #include <sys/types.h>
24 #include <sys/ioctl.h>
25 #include <sys/select.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <sys/wait.h>
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
31 #include <arpa/inet.h>
32 
33 #include <assert.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #if defined(__OpenBSD__)
43 /* for pty */
44 #include <termios.h>
45 #include <util.h>
46 #endif
47 
48 #if !defined(__linux__)
49 #define HAVE_SOCKADDR_LEN 1
50 #endif
51 
52 #define MIN(a, b) ((a) <= (b) ? (a) : (b))
53 
54 #define TEST_FIFO_NAME "iocond_fifo"
55 
56 enum filetype {
57 	FTYPE_NONE,
58 	FTYPE_FIFO,
59 	FTYPE_PIPE,
60 	FTYPE_PTY,
61 	FTYPE_SOCKET_TCP,
62 	FTYPE_SOCKET_UDP,
63 	FTYPE_SOCKET_UNIX,
64 };
65 
66 static struct {
67 	const char		*name;
68 	enum filetype		 type;
69 } filetypes[] = {
70 	{ "fifo",		FTYPE_FIFO },
71 	{ "pipe",		FTYPE_PIPE },
72 #if defined(__OpenBSD__)
73 	{ "pty",		FTYPE_PTY },
74 #endif
75 	{ "socket-tcp",		FTYPE_SOCKET_TCP },
76 	{ "socket-udp",		FTYPE_SOCKET_UDP },
77 	{ "socket-unix",	FTYPE_SOCKET_UNIX },
78 };
79 
80 static enum filetype filetype = FTYPE_NONE;
81 
82 static void cleanup(void);
83 static void proc_barrier(int);
84 static void proc_child(int, int);
85 static void proc_parent(int, int);
86 
87 int
88 main(int argc, char *argv[])
89 {
90 	const char *ftname;
91 	int bfd[2], fds[2];
92 	int child_fd = -1;
93 	int parent_fd = -1;
94 	int sock = -1;
95 	unsigned int i;
96 	pid_t pid;
97 
98 	/* Enforce test timeout. */
99 	alarm(10);
100 
101 	if (argc != 2) {
102 		fprintf(stderr, "usage: %s filetype\n", argv[0]);
103 		return 1;
104 	}
105 	ftname = argv[1];
106 
107 	for (i = 0; i < sizeof(filetypes) / sizeof(filetypes[0]); i++) {
108 		if (strcmp(ftname, filetypes[i].name) == 0) {
109 			filetype = filetypes[i].type;
110 			break;
111 		}
112 	}
113 	if (filetype == FTYPE_NONE)
114 		errx(1, "unknown filetype");
115 
116 	/* Open barrier sockets. */
117 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, bfd) == -1)
118 		err(1, "socketpair");
119 
120 	atexit(cleanup);
121 
122 	switch (filetype) {
123 	case FTYPE_FIFO:
124 		(void)unlink(TEST_FIFO_NAME);
125 		if (mkfifo(TEST_FIFO_NAME, 0644) == -1)
126 			err(1, "mkfifo");
127 		break;
128 	case FTYPE_PIPE:
129 		if (pipe(fds) == -1)
130 			err(1, "pipe");
131 		parent_fd = fds[0];
132 		child_fd = fds[1];
133 		break;
134 #if defined(__OpenBSD__)
135 	case FTYPE_PTY:
136 		if (openpty(&parent_fd, &child_fd, NULL, NULL, NULL) == -1)
137 			err(1, "openpty");
138 		break;
139 #endif
140 	case FTYPE_SOCKET_TCP: {
141 		struct sockaddr_in inaddr;
142 
143 		sock = socket(AF_INET, SOCK_STREAM, 0);
144 
145 		memset(&inaddr, 0, sizeof(inaddr));
146 #ifdef HAVE_SOCKADDR_LEN
147 		inaddr.sin_len = sizeof(inaddr);
148 #endif
149 		inaddr.sin_family = AF_INET;
150 		inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
151 		if (bind(sock, (struct sockaddr *)&inaddr,
152 		    sizeof(inaddr)) == -1)
153 			err(1, "bind");
154 		if (listen(sock, 1) == -1)
155 			err(1, "listen");
156 		break;
157 	    }
158 	case FTYPE_SOCKET_UDP: {
159 		struct sockaddr_in inaddr;
160 
161 		sock = socket(AF_INET, SOCK_DGRAM, 0);
162 
163 		memset(&inaddr, 0, sizeof(inaddr));
164 #ifdef HAVE_SOCKADDR_LEN
165 		inaddr.sin_len = sizeof(inaddr);
166 #endif
167 		inaddr.sin_family = AF_INET;
168 		inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
169 		if (bind(sock, (struct sockaddr *)&inaddr,
170 		    sizeof(inaddr)) == -1)
171 			err(1, "bind");
172 		break;
173 	    }
174 	case FTYPE_SOCKET_UNIX:
175 		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1)
176 			err(1, "socketpair");
177 		parent_fd = fds[0];
178 		child_fd = fds[1];
179 		break;
180 	default:
181 		errx(1, "unhandled filetype");
182 	}
183 
184 	pid = fork();
185 	switch (pid) {
186 	case -1:
187 		err(1, "fork");
188 	case 0:
189 		switch (filetype) {
190 		case FTYPE_FIFO:
191 			child_fd = open(TEST_FIFO_NAME, O_WRONLY);
192 			if (child_fd == -1)
193 				err(1, "child: open");
194 			break;
195 		case FTYPE_SOCKET_TCP: {
196 			struct sockaddr_in inaddr;
197 			socklen_t inaddrlen;
198 			int on = 1;
199 
200 			/* Get the bound address. */
201 			inaddrlen = sizeof(inaddr);
202 			if (getsockname(sock, (struct sockaddr *)&inaddr,
203 			    &inaddrlen) == -1)
204 				err(1, "child: getsockname");
205 
206 			child_fd = socket(AF_INET, SOCK_STREAM, 0);
207 			if (child_fd == -1)
208 				err(1, "child: socket");
209 			if (connect(child_fd, (struct sockaddr *)&inaddr,
210 			    sizeof(inaddr)) == -1)
211 				err(1, "child: connect");
212 			if (setsockopt(child_fd, IPPROTO_TCP, TCP_NODELAY,
213 			    &on, sizeof(on)) == -1)
214 				err(1, "child: setsockopt(TCP_NODELAY)");
215 			break;
216 		    }
217 		case FTYPE_SOCKET_UDP: {
218 			struct sockaddr_in inaddr;
219 			socklen_t inaddrlen;
220 
221 			/* Get the bound address. */
222 			inaddrlen = sizeof(inaddr);
223 			if (getsockname(sock, (struct sockaddr *)&inaddr,
224 			    &inaddrlen) == -1)
225 				err(1, "child: getsockname");
226 
227 			child_fd = socket(AF_INET, SOCK_DGRAM, 0);
228 			if (child_fd == -1)
229 				err(1, "child: socket");
230 			if (connect(child_fd, (struct sockaddr *)&inaddr,
231 			    sizeof(inaddr)) == -1)
232 				err(1, "child: connect");
233 			break;
234 		    }
235 		default:
236 			break;
237 		}
238 		if (parent_fd != -1) {
239 			close(parent_fd);
240 			parent_fd = -1;
241 		}
242 		if (sock != -1) {
243 			close(sock);
244 			sock = -1;
245 		}
246 		proc_child(child_fd, bfd[1]);
247 		_exit(0);
248 	default:
249 		switch (filetype) {
250 		case FTYPE_FIFO:
251 			parent_fd = open(TEST_FIFO_NAME, O_RDONLY);
252 			if (parent_fd == -1)
253 				err(1, "parent: open");
254 			break;
255 		case FTYPE_SOCKET_TCP: {
256 			int on = 1;
257 
258 			parent_fd = accept(sock, NULL, NULL);
259 			if (parent_fd == -1)
260 				err(1, "parent: accept");
261 			if (setsockopt(parent_fd, IPPROTO_TCP, TCP_NODELAY,
262 			    &on, sizeof(on)) == -1)
263 				err(1, "parent: setsockopt(TCP_NODELAY)");
264 			break;
265 		    }
266 		case FTYPE_SOCKET_UDP:
267 			parent_fd = sock;
268 			sock = -1;
269 			break;
270 		default:
271 			break;
272 		}
273 		if (child_fd != -1) {
274 			close(child_fd);
275 			child_fd = -1;
276 		}
277 		if (sock != -1) {
278 			close(sock);
279 			sock = -1;
280 		}
281 		proc_parent(parent_fd, bfd[0]);
282 		break;
283 	}
284 
285 	if (waitpid(pid, NULL, 0) == -1)
286 		err(1, "waitpid");
287 
288 	return 0;
289 }
290 
291 static void
292 cleanup(void)
293 {
294 	if (filetype == FTYPE_FIFO)
295 		(void)unlink(TEST_FIFO_NAME);
296 }
297 
298 static void
299 proc_barrier(int fd)
300 {
301 	int ret;
302 	char b = 0;
303 
304 	ret = write(fd, &b, 1);
305 	assert(ret == 1);
306 	ret = read(fd, &b, 1);
307 	assert(ret == 1);
308 }
309 
310 static void
311 fdset_init(fd_set *rfd, fd_set *wfd, fd_set *efd, int fd)
312 {
313 	FD_ZERO(rfd);
314 	FD_ZERO(wfd);
315 	FD_ZERO(efd);
316 	FD_SET(fd, rfd);
317 	FD_SET(fd, wfd);
318 	FD_SET(fd, efd);
319 }
320 
321 static void
322 proc_child(int fd, int bfd)
323 {
324 	char buf[1024];
325 	fd_set efd, rfd, wfd;
326 	struct timeval tv = { 0, 1 };
327 	struct timeval zerotv = { 0, 0 };
328 	size_t nbytes;
329 	int ret;
330 	char b = 0;
331 
332 	proc_barrier(bfd);
333 
334 	fdset_init(&rfd, &wfd, &efd, fd);
335 	ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
336 	assert(ret == 1);
337 	assert(FD_ISSET(fd, &rfd) == 0);
338 	assert(FD_ISSET(fd, &wfd) != 0);
339 	assert(FD_ISSET(fd, &efd) == 0);
340 
341 	proc_barrier(bfd);
342 
343 	ret = write(fd, &b, 1);
344 	assert(ret == 1);
345 
346 	proc_barrier(bfd);
347 
348 	fdset_init(&rfd, &wfd, &efd, fd);
349 	ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
350 	assert(ret == 1);
351 	assert(FD_ISSET(fd, &rfd) == 0);
352 	assert(FD_ISSET(fd, &wfd) != 0);
353 	assert(FD_ISSET(fd, &efd) == 0);
354 
355 	proc_barrier(bfd);
356 
357 	/* parent: read */
358 
359 	proc_barrier(bfd);
360 
361 	if (filetype != FTYPE_SOCKET_UDP) {
362 		/* write until full */
363 		memset(buf, 0, sizeof(buf));
364 		nbytes = 0;
365 		for (;;) {
366 			FD_ZERO(&wfd);
367 			FD_SET(fd, &wfd);
368 			ret = select(fd + 1, NULL, &wfd, NULL, &zerotv);
369 			if (ret == 0)
370 				break;
371 			assert(ret == 1);
372 			assert(FD_ISSET(fd, &wfd) != 0);
373 			ret = write(fd, buf, sizeof(buf));
374 			assert(ret > 0);
375 			nbytes += ret;
376 		}
377 		ret = write(bfd, &nbytes, sizeof(nbytes));
378 		assert(ret == sizeof(nbytes));
379 
380 		proc_barrier(bfd);
381 
382 		/* parent: read until empty */
383 	}
384 
385 	proc_barrier(bfd);
386 
387 	/* Test out-of-band data. */
388 	switch (filetype) {
389 #if defined(__OpenBSD__)
390 	case FTYPE_PTY: {
391 		/* parent: enable user ioctl command mode */
392 
393 		proc_barrier(bfd);
394 
395 		ret = write(fd, &b, 1);
396 		assert(ret == 1);
397 
398 		if (ioctl(fd, UIOCCMD(42), NULL) == -1)
399 			err(1, "child: ioctl(UIOCCMD)");
400 
401 		ret = write(fd, &b, 1);
402 		assert(ret == 1);
403 
404 		proc_barrier(bfd);
405 
406 		/* parent: read, and disable user ioctl command mode */
407 
408 		proc_barrier(bfd);
409 		break;
410 	    }
411 #endif /* __OpenBSD__ */
412 
413 	case FTYPE_SOCKET_TCP:
414 		ret = send(fd, &b, 2, 0);
415 		assert(ret == 2);
416 
417 		ret = send(fd, &b, 1, MSG_OOB);
418 		assert(ret == 1);
419 
420 		ret = send(fd, &b, 3, 0);
421 		assert(ret == 3);
422 
423 		proc_barrier(bfd);
424 
425 		/* parent: read */
426 
427 		proc_barrier(bfd);
428 		break;
429 
430 	default:
431 		break;
432 	}
433 
434 	/* Test socket shutdown. */
435 	switch (filetype) {
436 	case FTYPE_SOCKET_TCP:
437 	case FTYPE_SOCKET_UNIX:
438 		ret = write(fd, &b, 1);
439 		assert(ret == 1);
440 
441 		ret = shutdown(fd, SHUT_WR);
442 		assert(ret == 0);
443 
444 		proc_barrier(bfd);
445 
446 		/* parent: read and shutdown */
447 
448 		proc_barrier(bfd);
449 
450 		/* Let inet sockets take their time. */
451 		if (filetype == FTYPE_SOCKET_TCP)
452 			usleep(10000);
453 
454 		fdset_init(&rfd, &wfd, &efd, fd);
455 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
456 		assert(ret == 2);
457 		assert(FD_ISSET(fd, &rfd) != 0);
458 		assert(FD_ISSET(fd, &wfd) != 0);
459 		assert(FD_ISSET(fd, &efd) == 0);
460 		break;
461 
462 	case FTYPE_FIFO:
463 	case FTYPE_PIPE:
464 	case FTYPE_PTY:
465 	case FTYPE_SOCKET_UDP:
466 	default:
467 		break;
468 	}
469 
470 	proc_barrier(bfd);
471 
472 	close(fd);
473 
474 	proc_barrier(bfd);
475 
476 	fdset_init(&rfd, &wfd, &efd, fd);
477 	ret = select(fd + 1, &rfd, &wfd, &efd, NULL);
478 	assert(ret == -1);
479 	assert(errno == EBADF);
480 }
481 
482 static void
483 proc_parent(int fd, int bfd)
484 {
485 	char buf[1024];
486 	fd_set efd, rfd, wfd;
487 	struct timeval tv = { 0, 1 };
488 	struct timeval zerotv = { 0, 0 };
489 	size_t nbytes;
490 	int ret, retries;
491 	char b = 0;
492 
493 	proc_barrier(bfd);
494 
495 	fdset_init(&rfd, &wfd, &efd, fd);
496 	if (filetype == FTYPE_FIFO || filetype == FTYPE_PIPE)
497 		FD_CLR(fd, &wfd);
498 	ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
499 	switch (filetype) {
500 	case FTYPE_FIFO:
501 	case FTYPE_PIPE:
502 		assert(ret == 0);
503 		assert(FD_ISSET(fd, &rfd) == 0);
504 		assert(FD_ISSET(fd, &wfd) == 0);
505 		assert(FD_ISSET(fd, &efd) == 0);
506 		break;
507 	default:
508 		assert(ret == 1);
509 		assert(FD_ISSET(fd, &rfd) == 0);
510 		assert(FD_ISSET(fd, &wfd) != 0);
511 		assert(FD_ISSET(fd, &efd) == 0);
512 		break;
513 	}
514 
515 	proc_barrier(bfd);
516 
517 	/* child: write */
518 
519 	proc_barrier(bfd);
520 
521 	/* Let inet sockets take their time. */
522 	if (filetype == FTYPE_SOCKET_TCP ||
523 	    filetype == FTYPE_SOCKET_UDP)
524 		usleep(10000);
525 
526 	fdset_init(&rfd, &wfd, &efd, fd);
527 	if (filetype == FTYPE_FIFO || filetype == FTYPE_PIPE)
528 		FD_CLR(fd, &wfd);
529 	ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
530 	switch (filetype) {
531 	case FTYPE_FIFO:
532 	case FTYPE_PIPE:
533 		assert(ret == 1);
534 		assert(FD_ISSET(fd, &rfd) != 0);
535 		assert(FD_ISSET(fd, &wfd) == 0);
536 		assert(FD_ISSET(fd, &efd) == 0);
537 		break;
538 	case FTYPE_PTY:
539 	case FTYPE_SOCKET_TCP:
540 	case FTYPE_SOCKET_UDP:
541 	case FTYPE_SOCKET_UNIX:
542 		assert(ret == 2);
543 		assert(FD_ISSET(fd, &rfd) != 0);
544 		assert(FD_ISSET(fd, &wfd) != 0);
545 		assert(FD_ISSET(fd, &efd) == 0);
546 		break;
547 	default:
548 		assert(0);
549 	}
550 
551 	proc_barrier(bfd);
552 
553 	ret = read(fd, &b, 1);
554 	assert(ret == 1);
555 
556 	fdset_init(&rfd, &wfd, &efd, fd);
557 	ret = select(fd + 1, &rfd, NULL, NULL, &tv);
558 	assert(ret == 0);
559 
560 	proc_barrier(bfd);
561 
562 	if (filetype != FTYPE_SOCKET_UDP) {
563 		/* child: write until full */
564 		nbytes = 0;
565 		ret = read(bfd, &nbytes, sizeof(nbytes));
566 		assert(ret == sizeof(nbytes));
567 
568 		proc_barrier(bfd);
569 
570 		/* read until empty */
571 		retries = 5;
572 		while (retries > 0) {
573 			FD_ZERO(&rfd);
574 			FD_SET(fd, &rfd);
575 			ret = select(fd + 1, &rfd, NULL, NULL, &zerotv);
576 			if (ret == 0) {
577 				retries--;
578 				/* Let inet sockets take their time. */
579 				if (nbytes > 0 && retries > 0)
580 					usleep(10000);
581 				continue;
582 			}
583 			assert(ret == 1);
584 			assert(FD_ISSET(fd, &rfd) != 0);
585 			assert(nbytes > 0);
586 			ret = read(fd, buf, MIN(sizeof(buf), nbytes));
587 			assert(ret > 0);
588 			nbytes -= ret;
589 		}
590 		assert(nbytes == 0);
591 	}
592 
593 	proc_barrier(bfd);
594 
595 	/* Test out-of-band data. */
596 	switch (filetype) {
597 #if defined(__OpenBSD__)
598 	case FTYPE_PTY: {
599 		int off = 0;
600 		int on = 1;
601 
602 		if (ioctl(fd, TIOCUCNTL, &on) == -1)
603 			err(1, "parent: ioctl(TIOCUCNTL, 1)");
604 
605 		proc_barrier(bfd);
606 
607 		/* child: write */
608 
609 		proc_barrier(bfd);
610 
611 		fdset_init(&rfd, &wfd, &efd, fd);
612 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
613 		assert(ret == 3);
614 		assert(FD_ISSET(fd, &rfd) != 0);
615 		assert(FD_ISSET(fd, &wfd) != 0);
616 		assert(FD_ISSET(fd, &efd) != 0);
617 
618 		/* Read out-of-band data. */
619 		ret = read(fd, buf, sizeof(buf));
620 		assert(ret == 1);
621 
622 		fdset_init(&rfd, &wfd, &efd, fd);
623 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
624 		assert(ret == 2);
625 		assert(FD_ISSET(fd, &rfd) != 0);
626 		assert(FD_ISSET(fd, &wfd) != 0);
627 		assert(FD_ISSET(fd, &efd) == 0);
628 
629 		/* Read normal data. */
630 		ret = read(fd, buf, sizeof(buf));
631 		assert(ret == 3);
632 
633 		fdset_init(&rfd, &wfd, &efd, fd);
634 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
635 		assert(ret == 1);
636 		assert(FD_ISSET(fd, &rfd) == 0);
637 		assert(FD_ISSET(fd, &wfd) != 0);
638 		assert(FD_ISSET(fd, &efd) == 0);
639 
640 		if (ioctl(fd, TIOCUCNTL, &off) == -1)
641 			err(1, "parent: ioctl(TIOCUCNTL, 0)");
642 
643 		proc_barrier(bfd);
644 		break;
645 	    }
646 #endif /* __OpenBSD__ */
647 
648 	case FTYPE_SOCKET_TCP: {
649 		int atmark;
650 		int on = 1;
651 
652 		/* child: write */
653 
654 		if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &on,
655 		    sizeof(on)) == -1)
656 			err(1, "parent: setsockopt(SO_OOBINLINE)");
657 
658 		proc_barrier(bfd);
659 
660 		fdset_init(&rfd, &wfd, &efd, fd);
661 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
662 		assert(ret == 3);
663 		assert(FD_ISSET(fd, &rfd) != 0);
664 		assert(FD_ISSET(fd, &wfd) != 0);
665 		assert(FD_ISSET(fd, &efd) != 0);
666 
667 		/* Read normal data. */
668 		atmark = 0;
669 		if (ioctl(fd, SIOCATMARK, &atmark) == -1)
670 			err(1, "parent: ioctl(SIOCATMARK)");
671 		assert(atmark == 0);
672 		ret = recv(fd, buf, sizeof(buf), 0);
673 		assert(ret == 2);
674 
675 		fdset_init(&rfd, &wfd, &efd, fd);
676 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
677 		assert(ret == 3);
678 		assert(FD_ISSET(fd, &rfd) != 0);
679 		assert(FD_ISSET(fd, &wfd) != 0);
680 		assert(FD_ISSET(fd, &efd) != 0);
681 
682 		/* Read out-of-band data. */
683 		atmark = 0;
684 		if (ioctl(fd, SIOCATMARK, &atmark) == -1)
685 			err(1, "parent: ioctl(SIOCATMARK)");
686 		assert(atmark != 0);
687 		ret = recv(fd, &b, 1, 0);
688 		assert(ret == 1);
689 
690 		fdset_init(&rfd, &wfd, &efd, fd);
691 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
692 		assert(ret == 2);
693 		assert(FD_ISSET(fd, &rfd) != 0);
694 		assert(FD_ISSET(fd, &wfd) != 0);
695 		assert(FD_ISSET(fd, &efd) == 0);
696 
697 		/* Read normal data. */
698 		atmark = 0;
699 		if (ioctl(fd, SIOCATMARK, &atmark) == -1)
700 			err(1, "parent: ioctl(SIOCATMARK)");
701 		assert(atmark == 0);
702 		ret = recv(fd, buf, sizeof(buf), 0);
703 		assert(ret == 3);
704 
705 		fdset_init(&rfd, &wfd, &efd, fd);
706 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
707 		assert(ret == 1);
708 		assert(FD_ISSET(fd, &rfd) == 0);
709 		assert(FD_ISSET(fd, &wfd) != 0);
710 		assert(FD_ISSET(fd, &efd) == 0);
711 
712 		proc_barrier(bfd);
713 		break;
714 	    }
715 
716 	default:
717 		break;
718 	}
719 
720 	/* Test socket shutdown. */
721 	switch (filetype) {
722 	case FTYPE_SOCKET_TCP:
723 	case FTYPE_SOCKET_UNIX:
724 		/* child: write and shutdown */
725 
726 		proc_barrier(bfd);
727 
728 		/* Let inet sockets take their time. */
729 		if (filetype == FTYPE_SOCKET_TCP)
730 			usleep(10000);
731 
732 		fdset_init(&rfd, &wfd, &efd, fd);
733 		ret = select(fd + 1, &rfd, NULL, NULL, &tv);
734 		assert(ret == 1);
735 		assert(FD_ISSET(fd, &rfd) != 0);
736 
737 		ret = read(fd, &b, 1);
738 		assert(ret == 1);
739 
740 		fdset_init(&rfd, &wfd, &efd, fd);
741 		ret = select(fd + 1, &rfd, NULL, NULL, &tv);
742 		assert(ret == 1);
743 		assert(FD_ISSET(fd, &rfd) != 0);
744 
745 		ret = read(fd, &b, 1);
746 		assert(ret == 0);
747 
748 		ret = shutdown(fd, SHUT_WR);
749 		assert(ret == 0);
750 
751 		proc_barrier(bfd);
752 
753 		fdset_init(&rfd, &wfd, &efd, fd);
754 		ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
755 		assert(ret == 2);
756 		assert(FD_ISSET(fd, &rfd) != 0);
757 		assert(FD_ISSET(fd, &wfd) != 0);
758 		assert(FD_ISSET(fd, &efd) == 0);
759 		break;
760 
761 	case FTYPE_FIFO:
762 	case FTYPE_PIPE:
763 	case FTYPE_PTY:
764 	case FTYPE_SOCKET_UDP:
765 	default:
766 		break;
767 	}
768 
769 	proc_barrier(bfd);
770 
771 	/* child: close */
772 
773 	proc_barrier(bfd);
774 
775 	fdset_init(&rfd, &wfd, &efd, fd);
776 	if (filetype == FTYPE_FIFO || filetype == FTYPE_PIPE)
777 		FD_CLR(fd, &wfd);
778 	ret = select(fd + 1, &rfd, &wfd, &efd, &tv);
779 	switch (filetype) {
780 	case FTYPE_FIFO:
781 	case FTYPE_PIPE:
782 		assert(ret == 1);
783 		assert(FD_ISSET(fd, &rfd) != 0);
784 		assert(FD_ISSET(fd, &wfd) == 0);
785 		assert(FD_ISSET(fd, &efd) == 0);
786 		break;
787 	case FTYPE_PTY:
788 	case FTYPE_SOCKET_TCP:
789 	case FTYPE_SOCKET_UNIX:
790 		assert(ret == 2);
791 		assert(FD_ISSET(fd, &rfd) != 0);
792 		assert(FD_ISSET(fd, &wfd) != 0);
793 		assert(FD_ISSET(fd, &efd) == 0);
794 		break;
795 	case FTYPE_SOCKET_UDP:
796 		assert(ret == 1);
797 		assert(FD_ISSET(fd, &rfd) == 0);
798 		assert(FD_ISSET(fd, &wfd) != 0);
799 		assert(FD_ISSET(fd, &efd) == 0);
800 		break;
801 	default:
802 		assert(0);
803 	}
804 
805 	close(fd);
806 }
807