/*
 *
 * Copyright (c) International Business Machines  Corp., 2001
 *
 * This program is free software;  you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY;  without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program;  if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

/******************************************************************************

   File:        epoll-ltp.c

   Description:
     Test the epoll_* system calls. This test program attempts to
     be very thorough in exercising epoll_* system calls. Large
     combinations of valid and invalid parameters are passed with
     valid and invalid sequences. Due to the combinatorial nature
     of this test program the test may take a "long" time to
     execute.

   Total Tests: 2 (2 system calls are being tested for)

   Test Name:   epoll_create, epoll_ctl

   Test Assertion
   & Strategy:  Test a variety of incorrect parameters for epoll_create

                Then run a reasonable epoll_create and get a fd for the epoll
                     set.

                Next run epoll_ctl on that fd (epoll_fd) with a variety of
                     incorrect parameters and a couple correct ones.

                Finally ?How to thoroughly test epoll_wait?

   Author:      Matt Helsley <matthltc@us.ibm.com>

   History:     Created - May 22 2003 - Matt Helsley <matthltc@us.ibm.com>
                Added   -

   Notes: Currently we assume that the OS will never allocate an fd s.t.
          fd == INT_MAX and that it will instead choose to allocate fds
          from the "low" numbers. -MH

   Currently pokes epoll_create several times in 2 + NUM_RAND_ATTEMPTS ways
             pokes epoll_ctl 27648 - (2 + NUM_RAND_ATTEMPTS) ways
             does not poke epoll_wait

   TODO: change errno test code to build lists of possible errno values for
            each erroneous parameter. Check that the errno value is in one
            of the lists. Currently errno is not checked at all when multiple
            erroneous parameters are passed in.

         test epoll_ctl with a large number of file descriptor events in the
            set

   Link against epoll and ltp (-lepoll -lltp)

*******************************************************************************/

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <limits.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/wait.h>

#include "config.h"
#include "test.h"

char *TCID = "epoll01";
int TST_TOTAL = 1;

#ifdef HAVE_SYS_EPOLL_H

#include <sys/epoll.h>

/* Local Defines */
#if !defined(TRUE) && !defined(FALSE)
#define TRUE  1
#define FALSE 0
#endif

#define NUM_RAND_ATTEMPTS 16
#define BACKING_STORE_SIZE_HINT 32

/*
  Define the beginning of a "protected region".
  This is a region where a wide variety of errors
  could occur or signals could arrive (including
  SIGSEGV and SIGKILL).
$
  The test program uses this to catch those
  conditions as best it can and continue testing.

  The region MUST be marked by a corresponding
  PROTECT_REGION_END.

  DO NOT nest protected regions! i.e. Do not build
  code of the form:

	PROTECT_REGION_START
              ...
	PROTECT_REGION_START
              ...
	PROTECT_REGION_END
              ...
	PROTECT_REGION_END
 */
#define PROTECT_REGION_START		\
do {					\
	pid_t kid_pid;			\
	int kid_status;			\
					\
	tst_flush();			\
	kid_pid = FORK_OR_VFORK();	\
	if (kid_pid == 0) {

#define PROTECT_REGION_EXIT(errval) return (errval);

#define PROTECT_REGION_END(result, errval)					\
	return 0;								\
	} else {								\
	 waitpid(kid_pid, &kid_status, 0);					\
	 if (WIFEXITED(kid_status)) {						\
		(result) = WEXITSTATUS(kid_status);				\
	} else { /* Must have been signaled */					\
		(result) = (errval);						\
		if (WIFSIGNALED(kid_status))						\
			tst_resm(TFAIL, "Protected function test exitted due to signal %d (%s)", \
				WTERMSIG(kid_status), strsignal(WTERMSIG(kid_status)));	\
		}								\
	}									\
} while (0)

/*
 * Call a function in a "protected" context.
 * This protects the test program proper from segfaults
 * and allows for the extraction of an integer return
 * code.
 *
 * return only integer results.
 */
#define PROTECT_FUNC(fn, errval, epoll_fd) (					\
{										\
	pid_t kid_pid;								\
	int kid_status;								\
										\
	tst_flush();								\
	kid_pid = FORK_OR_VFORK();						\
	if (kid_pid == 0) { /* Run the function */				\
		return fn(epoll_fd);						\
	} else {								\
		waitpid(kid_pid, &kid_status, 0);				\
		if (WIFEXITED(kid_status)) {					\
		kid_status = WEXITSTATUS(kid_status);				\
	} else { /* Must have been signaled */					\
		kid_status = (errval);						\
		if (WIFSIGNALED(kid_status))					\
			tst_resm(TFAIL, "Protected function test exitted due to signal %d (%s)", \
						WTERMSIG(kid_status), strsignal(WTERMSIG(kid_status))); \
	}									\
}										\
kid_status = kid_status;})

/*
 * Given the number of random size requests to test,
 * test various boundary cases of epoll_create().
 *
 * Return the number of tests that failed. 0 indicates
 * 100% passed.
 */
int test_epoll_create(unsigned int num_rand_attempts)
{
	int epoll_fd = -1;
	int fd_set_size = -1;
	unsigned int attempt_count;
	unsigned int num_epoll_create_test_fails = 0;
	unsigned int num_epoll_create_test_calls = 0;

	/* Negative set sizes */
	errno = 0;
	fd_set_size = -1;
	num_epoll_create_test_calls++;
	epoll_fd = epoll_create(fd_set_size);
	if (epoll_fd >= 0) {
		tst_resm(TFAIL | TERRNO,
			 "epoll_create with negative set size succeeded unexpectedly");
		num_epoll_create_test_fails++;
		close(epoll_fd);
	} else {
		if (errno != EINVAL) {
			tst_resm(TFAIL | TERRNO,
				 "epoll_create with negative set size didn't set errno to EINVAL");
			num_epoll_create_test_fails++;
		} else {
			tst_resm(TPASS, "epoll_create with negative set size");
		}
	}

	/* Large set sizes -- try several less than or equal to INT_MAX by some
	   small amount (expect num_rand_attempts to be approximately the
	   amount we'd like to go below INT_MAX). */
	fd_set_size = INT_MAX;
	for (attempt_count = num_rand_attempts; attempt_count > 0;
	     attempt_count--, fd_set_size--) {
		num_epoll_create_test_calls++;
		epoll_fd = epoll_create(fd_set_size);
		if (epoll_fd == -1) {
			if (errno != ENOMEM) {
				tst_resm(TFAIL,
					 "epoll_create with large set size (size = %d)",
					 fd_set_size);
				num_epoll_create_test_fails++;
			} else {
				tst_resm(TPASS,
					 "epoll_create with large set size (size = %d)",
					 fd_set_size);
			}
		} else {
			tst_resm(TPASS,
				 "epoll_create with large set size (size = %d)",
				 fd_set_size);
			close(epoll_fd);
		}
	}

	/* Random large set sizes */
	for (attempt_count = num_rand_attempts; attempt_count > 0;
	     attempt_count--) {
		fd_set_size = abs(rand() + SHRT_MAX) % INT_MAX;
		errno = 0;
		num_epoll_create_test_calls++;
		epoll_fd = epoll_create(fd_set_size);
		if (epoll_fd < 0) {
			if (errno != ENOMEM) {
				tst_resm(TFAIL,
					 "epoll_create with random random large set size (size = %d)",
					 fd_set_size);
				num_epoll_create_test_fails++;
			} else {
				tst_resm(TPASS,
					 "epoll_create with random random large set size (size = %d)",
					 fd_set_size);
			}
		} else {
			tst_resm(TPASS,
				 "epoll_create with random large set size (size = %d)",
				 fd_set_size);
			close(epoll_fd);
		}
	}

	tst_resm(TINFO,
		 "Summary: Of %d tests, epoll_create failed %d (%3.0f%% passed).",
		 num_epoll_create_test_calls, num_epoll_create_test_fails,
		 ((float)
		  (num_epoll_create_test_calls - num_epoll_create_test_fails)
		  * 100.0f / (float)
		  num_epoll_create_test_calls));
	/* Return 0 on success. */

	return num_epoll_create_test_fails;
}

/* RES_PASS indicates a PASS result */
#define RES_PASS 0

/*
 * RES_FAIL_* indicates a FAIL result
 * In brief, there are two things that can go wrong in a
 * failiure. The return value (result = epoll_ctl(...)) and
 * the errno value may not match expectations. In this notation,
 * MIS -> mismatch, MAT -> match, BAD -> bad, and IGN -> ignored.
 *
 * RETV_MIS_* indicates the return value was either 0 or 1, but did
 * not match the expected return value
 *
 * _RETV_MAT_* indicates that the return value was 0 xor 1 and did
 * match the expected value
 *
 *_RETV_BAD_* the return value was neither 0 nor 1.
 *_ERRNO_MAT  the error number matched the expected number
 *_ERRNO_MIS  the error number did not match the expected number
 *_ERRNO_IGN  no error number was expected and so errno was ignored
 *
 * Keep these values below 256 as only 1 byte gets passed as a
 * return value for the process. Note that RES_PASS is 0 which
 * LTP interprets as a PASS.
 */

/* Did not get the expected return value, but errno value was expected */
#define RES_FAIL_RETV_MIS_ERRNO_MAT 1
/* Did not get the expected return value, but errno value was expected */
#define RES_FAIL_RETV_BAD_ERRNO_MAT 2
/* Did get the expected return value, and errno value was not expected */
#define RES_FAIL_RETV_MAT_ERRNO_MIS 3
/* Return value was neither 0 nor -1. Mismatch in value of errno */
#define RES_FAIL_RETV_BAD_ERRNO_MIS 4
/* Did not get the expected return value and errno is irrelevant */
#define RES_FAIL_RETV_MIS_ERRNO_IGN 5
/* Return value was neither 0 nor -1. value of errno is irrelevant */
#define RES_FAIL_RETV_BAD_ERRNO_IGN 6
/* We expected multiple errors so we were unable to check errno for conformance */
#define RES_PASS_RETV_MAT_ERRNO_IGN 7

static const char *result_strings[] = {
	"Passed",
	"Return value mismatched yet errno matched.",
	"Return value was bad    yet errno matched.",
	"Return value matched    yet errno mismatched.",
	"Return value was bad    and errno mismatched.",
	"Return value mismatched  so errno ignored.",
	"Return value was bad     so errno ignored.",
	"Return value matched    but errno ignored. (multiple errors expected)"
};

/****************************************************************************************/
/* This macro helps keep the code below understandable. It prints out the
   failiure message passed to it plus the parameters to the system call. */
#define EPOLL_CTL_TEST_RESULTS_SHOW_PARAMS 1
#if EPOLL_CTL_TEST_RESULTS_SHOW_PARAMS
#define EPOLL_CTL_TEST_FAIL(msg , ...) \
({ \
	if (ev_ptr != NULL) { \
		tst_resm(TFAIL, ( "(epoll_ctl(%d,%08x,%d,%p = {%08x,%08d}) returned %d:%s)" ) , ##__VA_ARGS__ , \
			epoll_fds[epfd_index], epoll_ctl_ops[op_index], \
			epoll_fds[fd_index], ev_ptr, ev_ptr->events, ev_ptr->data.fd, errno, \
			strerror(errno)); \
	} else { \
		tst_resm(TFAIL, ( "(epoll_ctl(%d,%08x,%d,%p) returned %d:%s)" ) , ##__VA_ARGS__  , \
			epoll_fds[epfd_index], epoll_ctl_ops[op_index], \
			epoll_fds[fd_index], ev_ptr, errno, strerror(errno)); \
	} \
})

#define EPOLL_CTL_TEST_PASS(msg , ...) \
({ \
	if (ev_ptr != NULL) { \
		tst_resm(TPASS, ( "(epoll_ctl(%d,%08x,%d,%p = {%08x,%08d}) returned %d:%s)" ) , ##__VA_ARGS__ , \
			epoll_fds[epfd_index], epoll_ctl_ops[op_index], \
			epoll_fds[fd_index], ev_ptr, ev_ptr->events, ev_ptr->data.fd, errno, \
			strerror(errno)); \
	} else { \
		tst_resm(TPASS, ( "(epoll_ctl(%d,%08x,%d,%p) returned %d:%s)" ) , ##__VA_ARGS__  , \
			epoll_fds[epfd_index], epoll_ctl_ops[op_index], \
			epoll_fds[fd_index], ev_ptr, errno, strerror(errno)); \
	} \
})
#else
#define EPOLL_CTL_TEST_FAIL(msg , ...) tst_resm(TFAIL, msg , ##__VA_ARGS__)
#define EPOLL_CTL_TEST_PASS(msg , ...) tst_resm(TPASS, msg , ##__VA_ARGS__)
#endif

/****************************************************************************************/

int test_epoll_ctl(int epoll_fd)
{
	int fds[] = { -1, INT_MAX };
	int epoll_fds[] = { 0, -1, 0, INT_MAX };
	int epoll_events[64];
	/* The list of operations to try AND THE ORDER THEY ARE TRIED IN */
	int epoll_ctl_ops[] =
	    { EPOLL_CTL_DEL, EPOLL_CTL_MOD, EPOLL_CTL_ADD, EPOLL_CTL_MOD,
		EPOLL_CTL_DEL, EPOLL_CTL_MOD, EPOLL_CTL_DEL, INT_MAX, -1
	};
	struct epoll_event event;
	char event_mem[sizeof(struct epoll_event) * 2];
	struct epoll_event *unaligned_event_ptr;

	/* Indices into lists */
	int index = 0;		/* multi-use index. First uses are to initialize
				   lists. Second use is to iterate over the implicit
				   list of structs to pass in */
	unsigned int epfd_index;	/* index into fd list for the epfd parameter */
	unsigned int event_index;	/* index into event list for the events field of the
					   struct epoll_event parameter */
	unsigned int fd_index;	/* index into fd list for the fd parameter */
	unsigned int op_index;	/* index into the list of operations for the op
				   parameter */
	unsigned int num_epoll_ctl_test_fails = 0;
	unsigned int num_epoll_ctl_test_calls = 0;

	/* Generate all possible combinations of events (2^6 == 64)
	   Assume we know nothing about the EPOLL event types _except_
	   that they describe bits in a set. */
	for (index = 0; index < 64; index++) {
		epoll_events[index] = ((EPOLLIN * ((index & 0x01) >> 0)) |
				       (EPOLLOUT * ((index & 0x02) >> 1)) |
				       (EPOLLPRI * ((index & 0x04) >> 2)) |
				       (EPOLLERR * ((index & 0x08) >> 3)) |
				       (EPOLLHUP * ((index & 0x10) >> 4)) |
				       (EPOLLET * ((index & 0x20) >> 5)));
	}

	/* Get a pointer to an unaligned struct epoll_event */
	{
		char *unalign_ptr = event_mem;

		unalign_ptr =
		    unalign_ptr + (((unsigned long)unalign_ptr & 1) ? 0 : 1);
		unaligned_event_ptr = (struct epoll_event *)unalign_ptr;
	}

	/* One of the fds we want to test is the valid one */
	epoll_fds[0] = epoll_fd;

	/* Test out all of the interesting combinations. This is going to
	   take a while (in compute cycles). It took less than 1 minute to
	   run on a PIII 500 without checking the results. */
	for (index = 0; index < 3; index++) {
		struct epoll_event *ev_ptr = NULL;

		switch (index) {
		case 0:	/* Pass aligned struct */
			event.data.u64 = 0;
			ev_ptr = &event;
			break;
		case 1:	/* Pass unaligned struct */
			unaligned_event_ptr->data.u64 = 0;
			ev_ptr = unaligned_event_ptr;
			break;
		case 2:
		default:	/* Pass NULL ptr */
			ev_ptr = NULL;
			break;
		}

		for (epfd_index = 0;
		     epfd_index < (sizeof(epoll_fds) / sizeof(int));
		     epfd_index++) {
			for (event_index = 0;
			     event_index < (sizeof(epoll_events) / sizeof(int));
			     event_index++) {
				for (fd_index = 0;
				     fd_index < (sizeof(fds) / sizeof(int));
				     fd_index++) {
					/* Now epoll_fd is a descriptor that references the set of
					   file descriptors we are interested in. Next we test epoll_ctl */
					for (op_index = 0;
					     op_index <
					     (sizeof(epoll_ctl_ops) /
					      sizeof(int)); op_index++) {
						int result;
						int expected_errno = 0;
						int num_errors_expected = 0;

						if (ev_ptr != NULL)
							ev_ptr->events =
							    epoll_events
							    [event_index];

						/* Perform the call itself. Put it in a protected region which
						   returns -1 in the variable result if a protection violation
						   occurs (see PROTECT_REGION_END for the result) */
						PROTECT_REGION_START errno = 0;

						/* NOTE that we are assuming that epoll will operate across
						   a fork() call such that a subsequent fork() in the parent
						   will also manipulate the same set */
						result =
						    epoll_ctl(epoll_fds
							      [epfd_index],
							      epoll_ctl_ops
							      [op_index],
							      fds[fd_index],
							      ev_ptr);

						/* We can't test errno resulting from the epoll_ctl call outside of
						   the PROTECT_REGION hence we do not have a PROTECT_REGION_END
						   here */

						/*
						   Test the results. Look for appropriate error conditions
						 */

						/* Check the epfd */
						if (epoll_fds[epfd_index] !=
						    epoll_fd) {
							/* Expect an error */
							if (epoll_fds
							    [epfd_index] == 0)
								expected_errno =
								    EINVAL;
							else	/* epfd is not a valid file descriptor since it is
								   neither epoll_fd nor stdin */
								expected_errno =
								    EBADF;
							num_errors_expected++;
						}

						switch (epoll_ctl_ops[op_index]) {
						case EPOLL_CTL_ADD:
						case EPOLL_CTL_MOD:
						case EPOLL_CTL_DEL:
							break;
						default:	/* Expect an error */
							expected_errno = EINVAL;
							num_errors_expected++;
							break;
						}

						expected_errno = EPERM;
						num_errors_expected++;

						if (ev_ptr == NULL) {
							expected_errno = EINVAL;
							num_errors_expected++;
						} else if ((ev_ptr == &event)
							   || (ev_ptr ==
							       unaligned_event_ptr))
						{
							if (ev_ptr->events == 0) {
								expected_errno =
								    EINVAL;
								num_errors_expected++;
							}

							for (index = 1;
							     index < 64;
							     index++) {
								if (ev_ptr->events != epoll_events[index]) {
									expected_errno
									    =
									    EINVAL;
									num_errors_expected++;
								}
							}
						} else {
							/* Do not expect an error */
						}

						if (num_errors_expected == 0) {
							/* We did not expect an error */
							if (result == 0) {
								/* We didn't get an error. Think of this as RES_PASS_RETV_MAT_ERRNO_IGN */
								return RES_PASS;
							} else if (result == -1) {	/* The return value is -1, so it's not bad */
								return
								    RES_FAIL_RETV_MIS_ERRNO_IGN;
							} else {
								return
								    RES_FAIL_RETV_BAD_ERRNO_IGN;
							}
						} else if (num_errors_expected
							   == 1) {
							/* We expected an error */
							if (result == 0) {
								return RES_FAIL_RETV_MIS_ERRNO_IGN;	/* Unexpected success */
							} else if (result == -1) {
								/* We got an error. Check errno */
								if (errno ==
								    expected_errno)
								{
									return RES_PASS;	/* think of this as RETV_MAT_ERRNO_MAT */
								} else {
									return
									    RES_FAIL_RETV_MAT_ERRNO_MIS;
								}
							} else {
								/* We got a bad return code! Interpret this as
								   getting an error and check errno. */
								if (errno ==
								    expected_errno)
									return
									    RES_FAIL_RETV_BAD_ERRNO_MAT;
								else
									return
									    RES_FAIL_RETV_BAD_ERRNO_MIS;
							}
						} else if (num_errors_expected >
							   1) {
							/* We expected multiple errors */
							if (result == 0) {
								return RES_FAIL_RETV_MIS_ERRNO_IGN;	/* Unexpected success */
							} else if (result == -1) {
								/* We got an error. Check errno */
								if (errno ==
								    expected_errno)
								{
									return RES_PASS;	/* think of this as RETV_MAT_ERRNO_MAT */
								} else {
									/* Ignore errno because the desired value is unknowable
									   without looking at the structure of the code. */
									return
									    RES_PASS_RETV_MAT_ERRNO_IGN;
								}
							} else {
								/* We got a bad return code! Interpret this as
								   getting an error and check errno. */
								if (errno ==
								    expected_errno)
									/* Don't Ignore errno because the desired value
									   happened to match what we expected. */
									return
									    RES_FAIL_RETV_BAD_ERRNO_MAT;
								else
									/* Ignore errno because the desired value is unknowable
									   without looking at the structure of the code. */
									return
									    RES_FAIL_RETV_BAD_ERRNO_IGN;
							}
						}

						/* All "return"s between PROTECT_REGION_BEGIN
						   and PROTECT_REGION_END place their value in
						   the result parameter. If the region caused
						   a protection violation (segfault or otherwise)
						   then the result is set to the second parameter's
						   value (-1 in this case). */
						PROTECT_REGION_END(result, -1);

						/* Count the number of tests run */
						num_epoll_ctl_test_calls++;

						/* Now test the result */
						if (!((result == RES_PASS)
						      || (result ==
							  RES_PASS_RETV_MAT_ERRNO_IGN)))
						{
							if (result >
							    (sizeof
							     (result_strings) /
							     sizeof(const char
								    *))) {
								/* Returned a result which has no corresponding text description */
								EPOLL_CTL_TEST_FAIL
								    ("FIXME FIX ME BUG in Test Program itself!");
							} else {
								if (result == -1)	/* Segfault during epoll_ctl call */
									EPOLL_CTL_TEST_FAIL
									    ("Test arguments caused abnormal exit.");
								else	/* The 'normal' failiure */
									EPOLL_CTL_TEST_FAIL
									    ((result_strings[result]));
							}
							num_epoll_ctl_test_fails++;
#ifdef DEBUG
						} else	/* The call of epoll_ctl behaved as expected */
							EPOLL_CTL_TEST_PASS((result_strings[result]));
#else
						}
#endif
					}
				}
			}
		}
		close(epoll_fd);
	}

	tst_resm(TINFO,
		 "Summary: Of %d tests, epoll_ctl failed %d (%3.0f%% passed).",
		 num_epoll_ctl_test_calls, num_epoll_ctl_test_fails,
		 ((float)(num_epoll_ctl_test_calls - num_epoll_ctl_test_fails) *
		  100.0f / (float)num_epoll_ctl_test_calls));
	return (num_epoll_ctl_test_fails / num_epoll_ctl_test_calls);
}

int main(int argc, char **argv)
{
	int epoll_fd;
	struct timeval tv;
	int last_result;

	tst_resm(TINFO, "testing if epoll() system call works");

	/* Get the current time */
	if (gettimeofday(&tv, NULL) != 0) {
		tst_brkm(TBROK | TERRNO, NULL, "gettimeofday failed");
	} else {
		tst_resm(TINFO, "gettimeofday() works");
	}

	/* Set up RNG */
	srand(tv.tv_usec);
	tst_resm(TINFO,
		 "random number seeded with gettimeofday() [seed = %ld] works",
		 tv.tv_usec);

	tst_resm(TINFO, "Testing epoll_create");
	/* Testing epoll_create with some different sizes */
	last_result = PROTECT_FUNC(test_epoll_create, -1, NUM_RAND_ATTEMPTS);
	if (last_result != 0) {
		/* create test(s) failed */
	}

	/* Create an epoll_fd for testing epoll_ctl */
	epoll_fd = epoll_create(BACKING_STORE_SIZE_HINT);
	if (epoll_fd < 0) {
		tst_brkm(TFAIL | TERRNO, NULL, "epoll_create failed");
	}

	tst_resm(TINFO, "Testing epoll_ctl");
	last_result = PROTECT_FUNC(test_epoll_ctl, -1, epoll_fd);
	if (last_result != 0) {
		/* ctl test(s) failed */
	}

	tst_exit();
}

#else

int main(void)
{
	tst_brkm(TCONF, NULL, "No epoll support found.");
}

#endif
