1 /* $OpenBSD: select_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 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
main(int argc,char * argv[])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
cleanup(void)292 cleanup(void)
293 {
294 if (filetype == FTYPE_FIFO)
295 (void)unlink(TEST_FIFO_NAME);
296 }
297
298 static void
proc_barrier(int fd)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
fdset_init(fd_set * rfd,fd_set * wfd,fd_set * efd,int fd)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
proc_child(int fd,int bfd)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, 1, 0);
415 assert(ret == 1);
416
417 ret = send(fd, &b, 1, MSG_OOB);
418 assert(ret == 1);
419
420 ret = send(fd, &b, 1, 0);
421 assert(ret == 1);
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
proc_parent(int fd,int bfd)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 == 1);
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 == 1);
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