// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Crackerjack Project., 2007-2008 ,Hitachi, Ltd * Author(s): Takahiro Yasui , * Yumiko Sugita , * Satoshi Fujiwara * Copyright (c) 2016 Linux Test Project */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include "lapi/syscalls.h" #include "ltp_signal.h" #include "tst_sig_proc.h" #include "tst_test.h" /* Older versions of glibc don't publish this constant's value. */ #ifndef POLLRDHUP #define POLLRDHUP 0x2000 #endif #define TYPE_NAME(x) .ttype = x, .desc = #x struct test_case { int ttype; /* test type (enum) */ const char *desc; /* test description (name) */ int ret; /* expected ret code */ int err; /* expected errno code */ short expect_revents; /* expected revents value */ unsigned int nfds; /* nfds ppoll parameter */ sigset_t *sigmask; /* sigmask ppoll parameter */ sigset_t *sigmask_cur; /* sigmask set for current process */ struct timespec *ts; /* ts ppoll parameter */ struct pollfd *fds; /* fds ppoll parameter */ int sigint_count; /* if > 0, spawn process to send SIGINT */ /* 'count' times to current process */ unsigned int sigint_delay; /* delay between SIGINT signals */ }; enum test_type { NORMAL, MASK_SIGNAL, TIMEOUT, FD_ALREADY_CLOSED, SEND_SIGINT, SEND_SIGINT_RACE_TEST, INVALID_NFDS, INVALID_FDS, }; static int fd1 = -1; static sigset_t sigmask_empty, sigmask_sigint; static struct pollfd fds_good[1], fds_already_closed[1]; static struct timespec ts_short = { .tv_sec = 0, .tv_nsec = 200000000, }; static struct timespec ts_long = { .tv_sec = 2, .tv_nsec = 0, }; /* Test cases * * test status of errors on man page * * EBADF can't check because EBADF never happen even though * fd was invalid. In this case, information of invalid * fd is set in revents * EFAULT v ('fds' array in the invalid address space) * EINTR v (a non blocked signal was caught) * EINVAL v ('nfds' is over the 'RLIMIT_NOFILE' value) * ENOMEM can't check because it's difficult to create no-memory */ static struct test_case tcase[] = { { TYPE_NAME(NORMAL), .expect_revents = POLLIN | POLLOUT, .ret = 1, .err = 0, .nfds = 1, .ts = &ts_long, .fds = fds_good, }, { TYPE_NAME(MASK_SIGNAL), .ret = 0, .err = 0, .nfds = 0, .sigmask = &sigmask_sigint, .ts = &ts_short, .fds = fds_good, .sigint_count = 4, .sigint_delay = 100000, }, { TYPE_NAME(TIMEOUT), .ret = 0, .err = 0, .nfds = 0, .ts = &ts_short, .fds = fds_good, }, { TYPE_NAME(FD_ALREADY_CLOSED), .expect_revents = POLLNVAL, .ret = 1, .err = 0, .nfds = 1, .ts = &ts_long, .fds = fds_already_closed, }, { TYPE_NAME(SEND_SIGINT), .ret = -1, .err = EINTR, .nfds = 0, .ts = &ts_long, .fds = fds_good, .sigint_count = 40, .sigint_delay = 100000, }, { TYPE_NAME(SEND_SIGINT_RACE_TEST), .ret = -1, .err = EINTR, .nfds = 0, .sigmask = &sigmask_empty, .sigmask_cur = &sigmask_sigint, .ts = &ts_long, .fds = fds_good, .sigint_count = 1, .sigint_delay = 0, }, { TYPE_NAME(INVALID_NFDS), .ret = -1, .err = EINVAL, .nfds = -1, .ts = &ts_long, .fds = fds_good, }, { TYPE_NAME(INVALID_FDS), .ret = -1, .err = EFAULT, .nfds = 1, .ts = &ts_long, .fds = (struct pollfd *) -1, }, }; static void sighandler(int sig LTP_ATTRIBUTE_UNUSED) { } static void setup(void) { int fd2; SAFE_SIGNAL(SIGINT, sighandler); if (sigemptyset(&sigmask_empty) == -1) tst_brk(TBROK | TERRNO, "sigemptyset"); if (sigemptyset(&sigmask_sigint) == -1) tst_brk(TBROK | TERRNO, "sigemptyset"); if (sigaddset(&sigmask_sigint, SIGINT) == -1) tst_brk(TBROK | TERRNO, "sigaddset"); fd1 = SAFE_OPEN("testfile1", O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); fds_good[0].fd = fd1; fds_good[0].events = POLLIN | POLLPRI | POLLOUT | POLLRDHUP; fds_good[0].revents = 0; fd2 = SAFE_OPEN("testfile2", O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); fds_already_closed[0].fd = fd2; fds_already_closed[0].events = POLLIN | POLLPRI | POLLOUT | POLLRDHUP; fds_already_closed[0].revents = 0; SAFE_CLOSE(fd2); } static void cleanup(void) { if (fd1 != -1) SAFE_CLOSE(fd1); } static void do_test(unsigned int i) { pid_t pid = 0; int sys_ret, sys_errno = 0, dummy; struct test_case *tc = &tcase[i]; struct timespec ts, *tsp = NULL; if (tc->ts) { memcpy(&ts, tc->ts, sizeof(ts)); tsp = &ts; } tst_res(TINFO, "case %s", tc->desc); /* setup */ if (tc->sigmask_cur) { if (sigprocmask(SIG_SETMASK, tc->sigmask_cur, NULL) == -1) tst_brk(TBROK, "sigprocmask"); } if (tc->sigint_count > 0) { pid = create_sig_proc(SIGINT, tc->sigint_count, tc->sigint_delay); } /* test */ errno = 0; sys_ret = tst_syscall(__NR_ppoll, tc->fds, tc->nfds, tsp, tc->sigmask, SIGSETSIZE); sys_errno = errno; /* cleanup */ if (tc->sigmask_cur) { if (sigprocmask(SIG_SETMASK, &sigmask_empty, NULL) == -1) tst_brk(TBROK, "sigprocmask"); } if (pid > 0) { kill(pid, SIGTERM); SAFE_WAIT(&dummy); } /* result check */ if (tc->expect_revents) { if (tc->fds[0].revents == tc->expect_revents) tst_res(TPASS, "revents=0x%04x", tc->expect_revents); else tst_res(TFAIL, "revents=0x%04x, expected=0x%04x", tc->fds[0].revents, tc->expect_revents); } if (tc->ret >= 0 && tc->ret == sys_ret) { tst_res(TPASS, "ret: %d", sys_ret); } else if (tc->ret == -1 && sys_ret == -1 && sys_errno == tc->err) { tst_res(TPASS, "ret: %d, errno: %s (%d)", sys_ret, tst_strerrno(sys_errno), sys_errno); } else { tst_res(TFAIL, "ret: %d, exp: %d, ret_errno: %s (%d)," " exp_errno: %s (%d)", sys_ret, tc->ret, tst_strerrno(sys_errno), sys_errno, tst_strerrno(tc->err), tc->err); } } static struct tst_test test = { .tcnt = ARRAY_SIZE(tcase), .test = do_test, .setup = setup, .cleanup = cleanup, .forks_child = 1, .needs_tmpdir = 1, };