xref: /freebsd/tools/regression/sockets/unix_gc/unix_gc.c (revision 3f0164abf32b9b761e0a2cb4bdca3a8b84f156d4)
1 /*-
2  * Copyright (c) 2007 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 /*
30  * A few regression tests for UNIX domain sockets.  Run from single-user mode
31  * as it checks the openfiles sysctl to look for leaks, and we don't want that
32  * changing due to other processes doing stuff.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/signal.h>
37 #include <sys/socket.h>
38 #include <sys/sysctl.h>
39 #include <sys/un.h>
40 #include <sys/wait.h>
41 
42 #include <netinet/in.h>
43 
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <limits.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 static int forcegc = 1;
54 static char dpath[PATH_MAX];
55 static const char *test;
56 
57 static int
58 getsysctl(const char *name)
59 {
60 	size_t len;
61 	int i;
62 
63 	len = sizeof(i);
64 	if (sysctlbyname(name, &i, &len, NULL, 0) < 0)
65 		err(-1, "%s", name);
66 	return (i);
67 }
68 
69 static int
70 getopenfiles(void)
71 {
72 
73 	return (getsysctl("kern.openfiles"));
74 }
75 
76 static int
77 getinflight(void)
78 {
79 
80 	return (getsysctl("net.local.inflight"));
81 }
82 
83 static int
84 getdeferred(void)
85 {
86 
87 	return (getsysctl("net.local.deferred"));
88 }
89 
90 static void
91 sendfd(int fd, int fdtosend)
92 {
93 	struct msghdr mh;
94 	struct message { struct cmsghdr msg_hdr; int fd; } m;
95 	ssize_t len;
96 	int after_inflight, before_inflight;
97 
98 	before_inflight = getinflight();
99 
100 	bzero(&mh, sizeof(mh));
101 	bzero(&m, sizeof(m));
102 	mh.msg_control = &m;
103 	mh.msg_controllen = sizeof(m);
104 	m.msg_hdr.cmsg_len = sizeof(m);
105 	m.msg_hdr.cmsg_level = SOL_SOCKET;
106 	m.msg_hdr.cmsg_type = SCM_RIGHTS;
107 	m.fd = fdtosend;
108 	len = sendmsg(fd, &mh, 0);
109 	if (len < 0)
110 		err(-1, "%s: sendmsg", test);
111 	after_inflight = getinflight();
112 	if (after_inflight != before_inflight + 1)
113 		errx(-1, "%s: sendfd: before %d after %d\n", test,
114 		    before_inflight, after_inflight);
115 }
116 
117 static void
118 close2(int fd1, int fd2)
119 {
120 
121 	close(fd1);
122 	close(fd2);
123 }
124 
125 static void
126 close3(int fd1, int fd2, int fd3)
127 {
128 
129 	close2(fd1, fd2);
130 	close(fd3);
131 }
132 
133 static void
134 close4(int fd1, int fd2, int fd3, int fd4)
135 {
136 
137 	close2(fd1, fd2);
138 	close2(fd3, fd4);
139 }
140 
141 static void
142 close5(int fd1, int fd2, int fd3, int fd4, int fd5)
143 {
144 
145 	close3(fd1, fd2, fd3);
146 	close2(fd4, fd5);
147 }
148 
149 static int
150 my_socket(int domain, int type, int proto)
151 {
152 	int sock;
153 
154 	sock = socket(domain, type, proto);
155 	if (sock < 0)
156 		err(-1, "%s: socket", test);
157 	return (sock);
158 }
159 
160 static void
161 my_bind(int sock, struct sockaddr *sa, socklen_t len)
162 {
163 
164 	if (bind(sock, sa, len) < 0)
165 		err(-1, "%s: bind", test);
166 }
167 
168 static void
169 my_connect(int sock, struct sockaddr *sa, socklen_t len)
170 {
171 
172 	if (connect(sock, sa, len) < 0 && errno != EINPROGRESS)
173 		err(-1, "%s: connect", test);
174 }
175 
176 static void
177 my_listen(int sock, int backlog)
178 {
179 
180 	if (listen(sock, backlog) < 0)
181 		err(-1, "%s: listen", test);
182 }
183 
184 static void
185 my_socketpair(int *sv)
186 {
187 
188 	if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
189 		err(-1, "%s: socketpair", test);
190 }
191 
192 static void
193 my_getsockname(int s, struct sockaddr *sa, socklen_t *salen)
194 {
195 
196 	if (getsockname(s, sa, salen) < 0)
197 		err(-1, "%s: getsockname", test);
198 }
199 
200 static void
201 setnonblock(int s)
202 {
203 
204 	if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
205 		err(-1, "%s: fcntl(F_SETFL, O_NONBLOCK)", test);
206 }
207 
208 static void
209 alloc3fds(int *s, int *sv)
210 {
211 
212 	if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
213 		err(-1, "%s: socket", test);
214 	if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) < 0)
215 		err(-1, "%s: socketpair", test);
216 }
217 
218 static void
219 alloc5fds(int *s, int *sva, int *svb)
220 {
221 
222 	if ((*s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
223 		err(-1, "%s: socket", test);
224 	if (socketpair(PF_UNIX, SOCK_STREAM, 0, sva) < 0)
225 		err(-1, "%s: socketpair", test);
226 	if (socketpair(PF_UNIX, SOCK_STREAM, 0, svb) < 0)
227 		err(-1, "%s: socketpair", test);
228 }
229 
230 static void
231 save_sysctls(int *before_inflight, int *before_openfiles)
232 {
233 
234 	*before_inflight = getinflight();
235 	*before_openfiles = getopenfiles();
236 }
237 
238 /*
239  * Try hard to make sure that the GC does in fact run before we test the
240  * condition of things.
241  */
242 static void
243 trigger_gc(void)
244 {
245 	int s;
246 
247 	if (forcegc) {
248 		if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
249 			err(-1, "trigger_gc: socket");
250 		close(s);
251 	}
252 	sleep(1);
253 }
254 
255 static void
256 test_sysctls(int before_inflight, int before_openfiles)
257 {
258 	int after_inflight, after_openfiles;
259 
260 	trigger_gc();
261 	after_inflight = getinflight();
262 	if (after_inflight != before_inflight)
263 		warnx("%s: before inflight: %d, after inflight: %d",
264 		    test, before_inflight, after_inflight);
265 
266 	after_openfiles = getopenfiles();
267 	if (after_openfiles != before_openfiles)
268 		warnx("%s: before: %d, after: %d", test, before_openfiles,
269 		    after_openfiles);
270 }
271 
272 static void
273 twosome_nothing(void)
274 {
275 	int inflight, openfiles;
276 	int sv[2];
277 
278 	/*
279 	 * Create a pair, close in one order.
280 	 */
281 	test = "twosome_nothing1";
282 	printf("%s\n", test);
283 	save_sysctls(&inflight, &openfiles);
284 	my_socketpair(sv);
285 	close2(sv[0], sv[1]);
286 	test_sysctls(inflight, openfiles);
287 
288 	/*
289 	 * Create a pair, close in the other order.
290 	 */
291 	test = "twosome_nothing2";
292 	printf("%s\n", test);
293 	save_sysctls(&inflight, &openfiles);
294 	my_socketpair(sv);
295 	close2(sv[0], sv[1]);
296 	test_sysctls(inflight, openfiles);
297 }
298 
299 /*
300  * Using a socket pair, send various endpoints over the pair and close in
301  * various orders.
302  */
303 static void
304 twosome_drop_work(const char *testname, int sendvia, int tosend, int closefirst)
305 {
306 	int inflight, openfiles;
307 	int sv[2];
308 
309 	printf("%s\n", testname);
310 	test = testname;
311 	save_sysctls(&inflight, &openfiles);
312 	my_socketpair(sv);
313 	sendfd(sv[sendvia], sv[tosend]);
314 	if (closefirst == 0)
315 		close2(sv[0], sv[1]);
316 	else
317 		close2(sv[1], sv[0]);
318 	test_sysctls(inflight, openfiles);
319 }
320 
321 static void
322 twosome_drop(void)
323 {
324 
325 	/*
326 	 * In various combations, some wastefully symmetric, create socket
327 	 * pairs and send one or another endpoint over one or another
328 	 * endpoint, closing the endpoints in various orders.
329 	 */
330 	twosome_drop_work("twosome_drop1", 0, 0, 0);
331 	twosome_drop_work("twosome_drop2", 0, 0, 1);
332 	twosome_drop_work("twosome_drop3", 0, 1, 0);
333 	twosome_drop_work("twosome_drop4", 0, 1, 1);
334 	twosome_drop_work("twosome_drop5", 1, 0, 0);
335 	twosome_drop_work("twosome_drop6", 1, 0, 1);
336 	twosome_drop_work("twosome_drop7", 1, 1, 0);
337 	twosome_drop_work("twosome_drop8", 1, 1, 1);
338 }
339 
340 static void
341 threesome_nothing(void)
342 {
343 	int inflight, openfiles;
344 	int s, sv[2];
345 
346 	test = "threesome_nothing";
347 	printf("%s\n", test);
348 	save_sysctls(&inflight, &openfiles);
349 	alloc3fds(&s, sv);
350 	close3(s, sv[0], sv[1]);
351 	test_sysctls(inflight, openfiles);
352 }
353 
354 /*
355  * threesome_drop: create a pair and a spare, send the spare over the pair, and
356  * close in various orders and make sure all the fds went away.
357  */
358 static void
359 threesome_drop(void)
360 {
361 	int inflight, openfiles;
362 	int s, sv[2];
363 
364 	/*
365 	 * threesome_drop1: close sent send receive
366 	 */
367 	test = "threesome_drop1";
368 	printf("%s\n", test);
369 	save_sysctls(&inflight, &openfiles);
370 	alloc3fds(&s, sv);
371 	sendfd(sv[0], s);
372 	close3(s, sv[0], sv[1]);
373 	test_sysctls(inflight, openfiles);
374 
375 	/*
376 	 * threesome_drop2: close sent receive send
377 	 */
378 	test = "threesome_drop2";
379 	printf("%s\n", test);
380 	save_sysctls(&inflight, &openfiles);
381 	alloc3fds(&s, sv);
382 	sendfd(sv[0], s);
383 	close3(s, sv[1], sv[0]);
384 	test_sysctls(inflight, openfiles);
385 
386 	/*
387 	 * threesome_drop3: close receive sent send
388 	 */
389 	test = "threesome_drop3";
390 	printf("%s\n", test);
391 	save_sysctls(&inflight, &openfiles);
392 	alloc3fds(&s, sv);
393 	sendfd(sv[0], s);
394 	close3(sv[1], s, sv[0]);
395 	test_sysctls(inflight, openfiles);
396 
397 	/*
398 	 * threesome_drop4: close receive send sent
399 	 */
400 	test = "threesome_drop4";
401 	printf("%s\n", test);
402 	save_sysctls(&inflight, &openfiles);
403 	alloc3fds(&s, sv);
404 	sendfd(sv[0], s);
405 	close3(sv[1], sv[0], s);
406 	test_sysctls(inflight, openfiles);
407 
408 	/*
409 	 * threesome_drop5: close send receive sent
410 	 */
411 	test = "threesome_drop5";
412 	printf("%s\n", test);
413 	save_sysctls(&inflight, &openfiles);
414 	alloc3fds(&s, sv);
415 	sendfd(sv[0], s);
416 	close3(sv[0], sv[1], s);
417 	test_sysctls(inflight, openfiles);
418 
419 	/*
420 	 * threesome_drop6: close send sent receive
421 	 */
422 	test = "threesome_drop6";
423 	printf("%s\n", test);
424 	save_sysctls(&inflight, &openfiles);
425 	alloc3fds(&s, sv);
426 	close3(sv[0], s, sv[1]);
427 	test_sysctls(inflight, openfiles);
428 }
429 
430 /*
431  * Fivesome tests: create two socket pairs and a spare, send the spare over
432  * the first socket pair, then send the first socket pair over the second
433  * socket pair, and GC.  Do various closes at various points to exercise
434  * various cases.
435  */
436 static void
437 fivesome_nothing(void)
438 {
439 	int inflight, openfiles;
440 	int spare, sva[2], svb[2];
441 
442 	test = "fivesome_nothing";
443 	printf("%s\n", test);
444 	save_sysctls(&inflight, &openfiles);
445 	alloc5fds(&spare, sva, svb);
446 	close5(spare, sva[0], sva[1], svb[0], svb[1]);
447 	test_sysctls(inflight, openfiles);
448 }
449 
450 static void
451 fivesome_drop_work(const char *testname, int close_spare_after_send,
452     int close_sva_after_send)
453 {
454 	int inflight, openfiles;
455 	int spare, sva[2], svb[2];
456 
457 	printf("%s\n", testname);
458 	test = testname;
459 	save_sysctls(&inflight, &openfiles);
460 	alloc5fds(&spare, sva, svb);
461 
462 	/*
463 	 * Send spare over sva.
464 	 */
465 	sendfd(sva[0], spare);
466 	if (close_spare_after_send)
467 		close(spare);
468 
469 	/*
470 	 * Send sva over svb.
471 	 */
472 	sendfd(svb[0], sva[0]);
473 	sendfd(svb[0], sva[1]);
474 	if (close_sva_after_send)
475 		close2(sva[0], sva[1]);
476 
477 	close2(svb[0], svb[1]);
478 
479 	if (!close_sva_after_send)
480 		close2(sva[0], sva[1]);
481 	if (!close_spare_after_send)
482 		close(spare);
483 
484 	test_sysctls(inflight, openfiles);
485 }
486 
487 static void
488 fivesome_drop(void)
489 {
490 
491 	fivesome_drop_work("fivesome_drop1", 0, 0);
492 	fivesome_drop_work("fivesome_drop2", 0, 1);
493 	fivesome_drop_work("fivesome_drop3", 1, 0);
494 	fivesome_drop_work("fivesome_drop4", 1, 1);
495 }
496 
497 /*
498  * Create a somewhat nasty dual-socket socket intended to upset the garbage
499  * collector if mark-and-sweep is wrong.
500  */
501 static void
502 complex_cycles(void)
503 {
504 	int inflight, openfiles;
505 	int spare, sva[2], svb[2];
506 
507 	test = "complex_cycles";
508 	printf("%s\n", test);
509 	save_sysctls(&inflight, &openfiles);
510 	alloc5fds(&spare, sva, svb);
511 	sendfd(sva[0], svb[0]);
512 	sendfd(sva[0], svb[1]);
513 	sendfd(svb[0], sva[0]);
514 	sendfd(svb[0], sva[1]);
515 	sendfd(svb[0], spare);
516 	sendfd(sva[0], spare);
517 	close5(spare, sva[0], sva[1], svb[0], svb[1]);
518 	test_sysctls(inflight, openfiles);
519 }
520 
521 /*
522  * Listen sockets can also be passed over UNIX domain sockets, so test
523  * various cases, including ones where listen sockets have waiting sockets
524  * hanging off them...
525  */
526 static void
527 listen_nothing(void)
528 {
529 	struct sockaddr_un sun;
530 	struct sockaddr_in sin;
531 	int inflight, openfiles;
532 	int s;
533 
534 	test = "listen_nothing_unp";
535 	printf("%s\n", test);
536 	bzero(&sun, sizeof(sun));
537 	sun.sun_family = AF_LOCAL;
538 	sun.sun_len = sizeof(sun);
539 	snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
540 	save_sysctls(&inflight, &openfiles);
541 	s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
542 	my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
543 	my_listen(s, -1);
544 	close(s);
545 	(void)unlink(sun.sun_path);
546 	test_sysctls(inflight, openfiles);
547 
548 	test = "listen_nothing_inet";
549 	printf("%s\n", test);
550 	bzero(&sin, sizeof(sin));
551 	sin.sin_family = AF_INET;
552 	sin.sin_len = sizeof(sin);
553 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
554 	sin.sin_port = htons(0);
555 	save_sysctls(&inflight, &openfiles);
556 	s = my_socket(PF_INET, SOCK_STREAM, 0);
557 	my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
558 	my_listen(s, -1);
559 	close(s);
560 	test_sysctls(inflight, openfiles);
561 }
562 
563 /*
564  * Send a listen UDP socket over a UNIX domain socket.
565  *
566  * Send a listen TCP socket over a UNIX domain socket.
567  *
568  * Do each twice, with closing of the listen socket vs. socketpair in
569  * different orders.
570  */
571 static void
572 listen_drop(void)
573 {
574 	struct sockaddr_un sun;
575 	struct sockaddr_in sin;
576 	int inflight, openfiles;
577 	int s, sv[2];
578 
579 	bzero(&sun, sizeof(sun));
580 	sun.sun_family = AF_LOCAL;
581 	sun.sun_len = sizeof(sun);
582 
583 	/*
584 	 * Close listen socket first.
585 	 */
586 	test = "listen_drop_unp1";
587 	printf("%s\n", test);
588 	snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
589 	save_sysctls(&inflight, &openfiles);
590 	s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
591 	my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
592 	my_listen(s, -1);
593 	my_socketpair(sv);
594 	sendfd(sv[0], s);
595 	close3(s, sv[0], sv[1]);
596 	test_sysctls(inflight, openfiles);
597 
598 	/*
599 	 * Close socketpair first.
600 	 */
601 	test = "listen_drop_unp2";
602 	printf("%s\n", test);
603 	snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/%s", dpath, test);
604 	save_sysctls(&inflight, &openfiles);
605 	s = my_socket(PF_LOCAL, SOCK_STREAM, 0);
606 	my_bind(s, (struct sockaddr *)&sun, sizeof(sun));
607 	my_listen(s, -1);
608 	my_socketpair(sv);
609 	sendfd(sv[0], s);
610 	close3(sv[0], sv[1], s);
611 	test_sysctls(inflight, openfiles);
612 
613 	sin.sin_family = AF_INET;
614 	sin.sin_len = sizeof(sin);
615 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
616 	sin.sin_port = htons(0);
617 
618 	/*
619 	 * Close listen socket first.
620 	 */
621 	test = "listen_drop_inet1";
622 	printf("%s\n", test);
623 	bzero(&sun, sizeof(sun));
624 	save_sysctls(&inflight, &openfiles);
625 	s = my_socket(PF_INET, SOCK_STREAM, 0);
626 	my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
627 	my_listen(s, -1);
628 	my_socketpair(sv);
629 	sendfd(sv[0], s);
630 	close3(s, sv[0], sv[1]);
631 	test_sysctls(inflight, openfiles);
632 
633 	/*
634 	 * Close socketpair first.
635 	 */
636 	test = "listen_drop_inet2";
637 	printf("%s\n", test);
638 	bzero(&sun, sizeof(sun));
639 	save_sysctls(&inflight, &openfiles);
640 	s = my_socket(PF_INET, SOCK_STREAM, 0);
641 	my_bind(s, (struct sockaddr *)&sin, sizeof(sin));
642 	my_listen(s, -1);
643 	my_socketpair(sv);
644 	sendfd(sv[0], s);
645 	close3(sv[0], sv[1], s);
646 	test_sysctls(inflight, openfiles);
647 }
648 
649 /*
650  * Up things a notch with listen sockets: add connections that can be
651  * accepted to the listen queues.
652  */
653 static void
654 listen_connect_nothing(void)
655 {
656 	struct sockaddr_in sin;
657 	int slisten, sconnect, sv[2];
658 	int inflight, openfiles;
659 	socklen_t len;
660 
661 	test = "listen_connect_nothing";
662 	printf("%s\n", test);
663 	save_sysctls(&inflight, &openfiles);
664 
665 	slisten = my_socket(PF_INET, SOCK_STREAM, 0);
666 	my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
667 	my_listen(slisten, -1);
668 
669 	my_socketpair(sv);
670 
671 	len = sizeof(sin);
672 	my_getsockname(slisten, (struct sockaddr *)&sin, &len);
673 
674 	sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
675 	setnonblock(sconnect);
676 	my_connect(sconnect, (struct sockaddr *)&sin, len);
677 
678 	sleep(1);
679 
680 	close4(slisten, sconnect, sv[0], sv[1]);
681 
682 	test_sysctls(inflight, openfiles);
683 }
684 
685 static void
686 listen_connect_drop(void)
687 {
688 	struct sockaddr_in sin;
689 	int slisten, sconnect, sv[2];
690 	int inflight, openfiles;
691 	socklen_t len;
692 
693 	test = "listen_connect_drop";
694 	printf("%s\n", test);
695 	save_sysctls(&inflight, &openfiles);
696 
697 	slisten = my_socket(PF_INET, SOCK_STREAM, 0);
698 	my_bind(slisten, (struct sockaddr *)&sin, sizeof(sin));
699 	my_listen(slisten, -1);
700 
701 	my_socketpair(sv);
702 
703 	len = sizeof(sin);
704 	my_getsockname(slisten, (struct sockaddr *)&sin, &len);
705 
706 	sconnect = my_socket(PF_INET, SOCK_STREAM, 0);
707 	setnonblock(sconnect);
708 	my_connect(sconnect, (struct sockaddr *)&sin, len);
709 
710 	sleep(1);
711 	sendfd(sv[0], slisten);
712 	close3(slisten, sv[0], sv[1]);
713 	sleep(1);
714 	close(sconnect);
715 
716 	test_sysctls(inflight, openfiles);
717 }
718 
719 static void
720 recursion(void)
721 {
722 	int fd[2], ff[2];
723 	int inflight, openfiles, deferred, deferred1;
724 
725 	test = "recursion";
726 	printf("%s\n", test);
727 	save_sysctls(&inflight, &openfiles);
728 	deferred = getdeferred();
729 
730 	my_socketpair(fd);
731 
732 	for (;;) {
733 		if (socketpair(PF_UNIX, SOCK_STREAM, 0, ff) == -1) {
734 			if (errno == EMFILE || errno == ENFILE)
735 				break;
736 			err(-1, "socketpair");
737 		}
738 		sendfd(ff[0], fd[0]);
739 		sendfd(ff[0], fd[1]);
740 		close2(fd[1], fd[0]);
741 		fd[0] = ff[0];
742 		fd[1] = ff[1];
743 	}
744 	close2(fd[0], fd[1]);
745 	sleep(1);
746 	test_sysctls(inflight, openfiles);
747 	deferred1 = getdeferred();
748 	if (deferred != deferred1)
749 		errx(-1, "recursion: deferred before %d after %d", deferred,
750 		    deferred1);
751 }
752 
753 #define	RMDIR	"rm -Rf "
754 int
755 main(int argc, char *argv[])
756 {
757 	char cmd[sizeof(RMDIR) + PATH_MAX];
758 	int serrno;
759 	pid_t pid;
760 
761 	strlcpy(dpath, "/tmp/unpgc.XXXXXXXX", sizeof(dpath));
762 	if (mkdtemp(dpath) == NULL)
763 		err(-1, "mkdtemp");
764 
765 	/*
766 	 * Set up a parent process to GC temporary storage when we're done.
767 	 */
768 	pid = fork();
769 	if (pid < 0) {
770 		serrno = errno;
771 		(void)rmdir(dpath);
772 		errno = serrno;
773 		err(-1, "fork");
774 	}
775 	if (pid > 0) {
776 		signal(SIGINT, SIG_IGN);
777 		while (waitpid(pid, NULL, 0) != pid);
778 		snprintf(cmd, sizeof(cmd), "%s %s", RMDIR, dpath);
779 		(void)system(cmd);
780 		exit(0);
781 	}
782 
783 	printf("Start: inflight %d open %d\n", getinflight(),
784 	    getopenfiles());
785 
786 	twosome_nothing();
787 	twosome_drop();
788 
789 	threesome_nothing();
790 	threesome_drop();
791 
792 	fivesome_nothing();
793 	fivesome_drop();
794 
795 	complex_cycles();
796 
797 	listen_nothing();
798 	listen_drop();
799 
800 	listen_connect_nothing();
801 	listen_connect_drop();
802 
803 	recursion();
804 
805 	printf("Finish: inflight %d open %d\n", getinflight(),
806 	    getopenfiles());
807 	return (0);
808 }
809