#include <sys/resource.h>
#include <sys/select.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <libcasper.h>
#include <atf-c.h>
#define NCONNECTIONS (FD_SETSIZE + 64)
#define FD_HEADROOM 64
ATF_TC_WITHOUT_HEAD(many_connections);
ATF_TC_BODY(many_connections, tc)
{
struct rlimit rl;
cap_channel_t *chan;
cap_channel_t **clones;
size_t i;
if (getrlimit(RLIMIT_NOFILE, &rl) != 0)
atf_tc_skip("getrlimit: %s", strerror(errno));
if (rl.rlim_max < NCONNECTIONS + FD_HEADROOM)
atf_tc_skip("RLIMIT_NOFILE hard cap %ju below required %d",
(uintmax_t)rl.rlim_max, NCONNECTIONS + FD_HEADROOM);
rl.rlim_cur = rl.rlim_max;
ATF_REQUIRE_MSG(setrlimit(RLIMIT_NOFILE, &rl) == 0,
"setrlimit: %s", strerror(errno));
chan = cap_init();
ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno));
clones = calloc(NCONNECTIONS, sizeof(*clones));
ATF_REQUIRE(clones != NULL);
for (i = 0; i < NCONNECTIONS; i++) {
clones[i] = cap_clone(chan);
ATF_REQUIRE_MSG(clones[i] != NULL,
"cap_clone failed at %zu/%d: %s",
i, NCONNECTIONS, strerror(errno));
}
for (i = 0; i < NCONNECTIONS; i++)
cap_close(clones[i]);
free(clones);
cap_close(chan);
}
#define CHURN_CONNECTIONS 50
#define CHURN_CLOSE_STEP 5
ATF_TC_WITHOUT_HEAD(connection_churn);
ATF_TC_BODY(connection_churn, tc)
{
cap_channel_t *chan, *survivor, *extra;
cap_channel_t *clones[CHURN_CONNECTIONS];
size_t i, survivor_idx;
chan = cap_init();
ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno));
for (i = 0; i < CHURN_CONNECTIONS; i++) {
clones[i] = cap_clone(chan);
ATF_REQUIRE_MSG(clones[i] != NULL,
"cap_clone failed at %zu: %s", i, strerror(errno));
}
for (i = 0; i < CHURN_CONNECTIONS; i += CHURN_CLOSE_STEP) {
cap_close(clones[i]);
clones[i] = NULL;
}
extra = cap_clone(chan);
ATF_REQUIRE_MSG(extra != NULL, "cap_clone after churn failed: %s",
strerror(errno));
survivor_idx = 1;
survivor = cap_clone(clones[survivor_idx]);
ATF_REQUIRE_MSG(survivor != NULL,
"cap_clone on survivor failed: %s", strerror(errno));
cap_close(survivor);
cap_close(extra);
for (i = 0; i < CHURN_CONNECTIONS; i++) {
if (clones[i] != NULL)
cap_close(clones[i]);
}
cap_close(chan);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, many_connections);
ATF_TP_ADD_TC(tp, connection_churn);
return (atf_no_error());
}