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