xref: /freebsd/lib/libcasper/tests/cap_main_test.c (revision a10bc81d333e04664c1a1d6024c580794b079eca)
1*a10bc81dSMariusz Zaborski /*-
2*a10bc81dSMariusz Zaborski  * SPDX-License-Identifier: BSD-2-Clause
3*a10bc81dSMariusz Zaborski  *
4*a10bc81dSMariusz Zaborski  * Copyright (c) 2026 Mariusz Zaborski <oshogbo@FreeBSD.org>
5*a10bc81dSMariusz Zaborski  *
6*a10bc81dSMariusz Zaborski  * Redistribution and use in source and binary forms, with or without
7*a10bc81dSMariusz Zaborski  * modification, are permitted provided that the following conditions
8*a10bc81dSMariusz Zaborski  * are met:
9*a10bc81dSMariusz Zaborski  * 1. Redistributions of source code must retain the above copyright
10*a10bc81dSMariusz Zaborski  *    notice, this list of conditions and the following disclaimer.
11*a10bc81dSMariusz Zaborski  * 2. Redistributions in binary form must reproduce the above copyright
12*a10bc81dSMariusz Zaborski  *    notice, this list of conditions and the following disclaimer in the
13*a10bc81dSMariusz Zaborski  *    documentation and/or other materials provided with the distribution.
14*a10bc81dSMariusz Zaborski  *
15*a10bc81dSMariusz Zaborski  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16*a10bc81dSMariusz Zaborski  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*a10bc81dSMariusz Zaborski  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*a10bc81dSMariusz Zaborski  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19*a10bc81dSMariusz Zaborski  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*a10bc81dSMariusz Zaborski  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*a10bc81dSMariusz Zaborski  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*a10bc81dSMariusz Zaborski  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*a10bc81dSMariusz Zaborski  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*a10bc81dSMariusz Zaborski  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*a10bc81dSMariusz Zaborski  * SUCH DAMAGE.
26*a10bc81dSMariusz Zaborski  */
27*a10bc81dSMariusz Zaborski 
28*a10bc81dSMariusz Zaborski #include <sys/resource.h>
29*a10bc81dSMariusz Zaborski #include <sys/select.h>
30*a10bc81dSMariusz Zaborski 
31*a10bc81dSMariusz Zaborski #include <errno.h>
32*a10bc81dSMariusz Zaborski #include <inttypes.h>
33*a10bc81dSMariusz Zaborski #include <stdlib.h>
34*a10bc81dSMariusz Zaborski #include <string.h>
35*a10bc81dSMariusz Zaborski 
36*a10bc81dSMariusz Zaborski #include <libcasper.h>
37*a10bc81dSMariusz Zaborski 
38*a10bc81dSMariusz Zaborski #include <atf-c.h>
39*a10bc81dSMariusz Zaborski 
40*a10bc81dSMariusz Zaborski #define	NCONNECTIONS	(FD_SETSIZE + 64)
41*a10bc81dSMariusz Zaborski #define	FD_HEADROOM	64
42*a10bc81dSMariusz Zaborski 
43*a10bc81dSMariusz Zaborski /* Test that file descriptors past FD_SETSIZE (1024) work. */
44*a10bc81dSMariusz Zaborski ATF_TC_WITHOUT_HEAD(many_connections);
ATF_TC_BODY(many_connections,tc)45*a10bc81dSMariusz Zaborski ATF_TC_BODY(many_connections, tc)
46*a10bc81dSMariusz Zaborski {
47*a10bc81dSMariusz Zaborski 	struct rlimit rl;
48*a10bc81dSMariusz Zaborski 	cap_channel_t *chan;
49*a10bc81dSMariusz Zaborski 	cap_channel_t **clones;
50*a10bc81dSMariusz Zaborski 	size_t i;
51*a10bc81dSMariusz Zaborski 
52*a10bc81dSMariusz Zaborski 	if (getrlimit(RLIMIT_NOFILE, &rl) != 0)
53*a10bc81dSMariusz Zaborski 		atf_tc_skip("getrlimit: %s", strerror(errno));
54*a10bc81dSMariusz Zaborski 	if (rl.rlim_max < NCONNECTIONS + FD_HEADROOM)
55*a10bc81dSMariusz Zaborski 		atf_tc_skip("RLIMIT_NOFILE hard cap %ju below required %d",
56*a10bc81dSMariusz Zaborski 		    (uintmax_t)rl.rlim_max, NCONNECTIONS + FD_HEADROOM);
57*a10bc81dSMariusz Zaborski 	rl.rlim_cur = rl.rlim_max;
58*a10bc81dSMariusz Zaborski 	ATF_REQUIRE_MSG(setrlimit(RLIMIT_NOFILE, &rl) == 0,
59*a10bc81dSMariusz Zaborski 	    "setrlimit: %s", strerror(errno));
60*a10bc81dSMariusz Zaborski 
61*a10bc81dSMariusz Zaborski 	chan = cap_init();
62*a10bc81dSMariusz Zaborski 	ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno));
63*a10bc81dSMariusz Zaborski 
64*a10bc81dSMariusz Zaborski 	clones = calloc(NCONNECTIONS, sizeof(*clones));
65*a10bc81dSMariusz Zaborski 	ATF_REQUIRE(clones != NULL);
66*a10bc81dSMariusz Zaborski 
67*a10bc81dSMariusz Zaborski 	/*
68*a10bc81dSMariusz Zaborski 	 * Every cap_clone(3) adds one more connection to the helper.
69*a10bc81dSMariusz Zaborski 	 * After this loop the helper is watching more fds than an
70*a10bc81dSMariusz Zaborski 	 * fd_set can hold.
71*a10bc81dSMariusz Zaborski 	 */
72*a10bc81dSMariusz Zaborski 	for (i = 0; i < NCONNECTIONS; i++) {
73*a10bc81dSMariusz Zaborski 		clones[i] = cap_clone(chan);
74*a10bc81dSMariusz Zaborski 		ATF_REQUIRE_MSG(clones[i] != NULL,
75*a10bc81dSMariusz Zaborski 		    "cap_clone failed at %zu/%d: %s",
76*a10bc81dSMariusz Zaborski 		    i, NCONNECTIONS, strerror(errno));
77*a10bc81dSMariusz Zaborski 	}
78*a10bc81dSMariusz Zaborski 
79*a10bc81dSMariusz Zaborski 	for (i = 0; i < NCONNECTIONS; i++)
80*a10bc81dSMariusz Zaborski 		cap_close(clones[i]);
81*a10bc81dSMariusz Zaborski 	free(clones);
82*a10bc81dSMariusz Zaborski 	cap_close(chan);
83*a10bc81dSMariusz Zaborski }
84*a10bc81dSMariusz Zaborski 
85*a10bc81dSMariusz Zaborski #define	CHURN_CONNECTIONS	50
86*a10bc81dSMariusz Zaborski #define	CHURN_CLOSE_STEP	5
87*a10bc81dSMariusz Zaborski 
88*a10bc81dSMariusz Zaborski /* Test that gaps in the file descriptor list do not break casper. */
89*a10bc81dSMariusz Zaborski ATF_TC_WITHOUT_HEAD(connection_churn);
ATF_TC_BODY(connection_churn,tc)90*a10bc81dSMariusz Zaborski ATF_TC_BODY(connection_churn, tc)
91*a10bc81dSMariusz Zaborski {
92*a10bc81dSMariusz Zaborski 	cap_channel_t *chan, *survivor, *extra;
93*a10bc81dSMariusz Zaborski 	cap_channel_t *clones[CHURN_CONNECTIONS];
94*a10bc81dSMariusz Zaborski 	size_t i, survivor_idx;
95*a10bc81dSMariusz Zaborski 
96*a10bc81dSMariusz Zaborski 	chan = cap_init();
97*a10bc81dSMariusz Zaborski 	ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno));
98*a10bc81dSMariusz Zaborski 
99*a10bc81dSMariusz Zaborski 	for (i = 0; i < CHURN_CONNECTIONS; i++) {
100*a10bc81dSMariusz Zaborski 		clones[i] = cap_clone(chan);
101*a10bc81dSMariusz Zaborski 		ATF_REQUIRE_MSG(clones[i] != NULL,
102*a10bc81dSMariusz Zaborski 		    "cap_clone failed at %zu: %s", i, strerror(errno));
103*a10bc81dSMariusz Zaborski 	}
104*a10bc81dSMariusz Zaborski 
105*a10bc81dSMariusz Zaborski 	/*
106*a10bc81dSMariusz Zaborski 	 * Close every Nth clone.
107*a10bc81dSMariusz Zaborski 	 */
108*a10bc81dSMariusz Zaborski 	for (i = 0; i < CHURN_CONNECTIONS; i += CHURN_CLOSE_STEP) {
109*a10bc81dSMariusz Zaborski 		cap_close(clones[i]);
110*a10bc81dSMariusz Zaborski 		clones[i] = NULL;
111*a10bc81dSMariusz Zaborski 	}
112*a10bc81dSMariusz Zaborski 
113*a10bc81dSMariusz Zaborski 	/*
114*a10bc81dSMariusz Zaborski 	 * Force a poll() cycle: the helper handles POLLIN on chan and
115*a10bc81dSMariusz Zaborski 	 * POLLHUP on the closed clones in the same walk.
116*a10bc81dSMariusz Zaborski 	 */
117*a10bc81dSMariusz Zaborski 	extra = cap_clone(chan);
118*a10bc81dSMariusz Zaborski 	ATF_REQUIRE_MSG(extra != NULL, "cap_clone after churn failed: %s",
119*a10bc81dSMariusz Zaborski 	    strerror(errno));
120*a10bc81dSMariusz Zaborski 
121*a10bc81dSMariusz Zaborski 	/* A surviving clone must still round-trip. */
122*a10bc81dSMariusz Zaborski 	survivor_idx = 1;
123*a10bc81dSMariusz Zaborski 	survivor = cap_clone(clones[survivor_idx]);
124*a10bc81dSMariusz Zaborski 	ATF_REQUIRE_MSG(survivor != NULL,
125*a10bc81dSMariusz Zaborski 	    "cap_clone on survivor failed: %s", strerror(errno));
126*a10bc81dSMariusz Zaborski 
127*a10bc81dSMariusz Zaborski 	cap_close(survivor);
128*a10bc81dSMariusz Zaborski 	cap_close(extra);
129*a10bc81dSMariusz Zaborski 	for (i = 0; i < CHURN_CONNECTIONS; i++) {
130*a10bc81dSMariusz Zaborski 		if (clones[i] != NULL)
131*a10bc81dSMariusz Zaborski 			cap_close(clones[i]);
132*a10bc81dSMariusz Zaborski 	}
133*a10bc81dSMariusz Zaborski 	cap_close(chan);
134*a10bc81dSMariusz Zaborski }
135*a10bc81dSMariusz Zaborski 
ATF_TP_ADD_TCS(tp)136*a10bc81dSMariusz Zaborski ATF_TP_ADD_TCS(tp)
137*a10bc81dSMariusz Zaborski {
138*a10bc81dSMariusz Zaborski 
139*a10bc81dSMariusz Zaborski 	ATF_TP_ADD_TC(tp, many_connections);
140*a10bc81dSMariusz Zaborski 	ATF_TP_ADD_TC(tp, connection_churn);
141*a10bc81dSMariusz Zaborski 	return (atf_no_error());
142*a10bc81dSMariusz Zaborski }
143