| /* |
| * Copyright (c) Jiri Palecek<jpalecek@web.de>, 2009 |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it would be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| * |
| * Further, this software is distributed without any warranty that it is |
| * free of the rightful claim of any third person regarding infringement |
| * or the like. Any license provided herein, whether implied or |
| * otherwise, applies only to this software file. Patent licenses, if |
| * any, provided herein do not apply to combinations of this program with |
| * other software, or any other product whatsoever. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| #include "test.h" |
| #include "usctest.h" |
| #include <errno.h> |
| #include <signal.h> |
| #include "../utils/include_j_h.h" |
| #include "../utils/common_j_h.c" |
| #include <limits.h> |
| #include "linux_syscall_numbers.h" |
| |
| #define SUCCEED_OR_DIE(syscall, message, ...) \ |
| (errno = 0, \ |
| ({int ret=syscall(__VA_ARGS__); \ |
| if (ret==-1) \ |
| tst_brkm(TBROK|TERRNO, cleanup, message);\ |
| ret;})) |
| |
| /* Report success iff TEST_RETURN and TEST_ERRNO are equal to |
| exp_return and exp_errno, resp., and cond is true. If cond is not |
| true, report condition_errmsg |
| */ |
| static void report_success_cond(const char *func, const char *file, int line, |
| long exp_return, int exp_errno, int condition, |
| char *condition_errmsg) |
| { |
| if (exp_return == TEST_RETURN |
| && (exp_return != -1 || exp_errno == TEST_ERRNO)) |
| if (condition) |
| tst_resm(TPASS, "Test passed"); |
| else |
| tst_resm(TFAIL, "%s (%s: %d): %s", func, file, line, |
| condition_errmsg); |
| else if (TEST_RETURN != -1) |
| tst_resm(TFAIL, |
| "%s (%s: %d): Unexpected return value; expected %ld, got %ld", |
| func, file, line, exp_return, TEST_RETURN); |
| else |
| tst_resm(TFAIL | TTERRNO, "%s (%s: %d): Unexpected failure", |
| func, file, line); |
| } |
| |
| #define REPORT_SUCCESS_COND(exp_return, exp_errno, condition, condition_errmsg) \ |
| report_success_cond(__FUNCTION__, __FILE__, __LINE__, exp_return, exp_errno, condition, condition_errmsg); |
| |
| /* Report success iff TEST_RETURN and TEST_ERRNO are equal to |
| exp_return and exp_errno, resp. |
| */ |
| #define REPORT_SUCCESS(exp_return, exp_errno) \ |
| REPORT_SUCCESS_COND(exp_return, exp_errno, 1, ""); |
| |
| static void cleanup(void); |
| |
| static void empty_handler(int sig) |
| { |
| } |
| |
| static void setup(void) |
| { |
| tst_sig(FORK, DEF_HANDLER, cleanup); |
| signal(SIGUSR1, empty_handler); |
| signal(SIGALRM, empty_handler); |
| signal(SIGUSR2, SIG_IGN); |
| |
| TEST_PAUSE; |
| } |
| |
| static void cleanup(void) |
| { |
| |
| TEST_CLEANUP; |
| } |
| |
| typedef int (*swi_func) (const sigset_t * set, siginfo_t * info, |
| struct timespec * timeout); |
| typedef void (*test_func) (swi_func, int); |
| |
| #ifdef TEST_SIGWAIT |
| static int my_sigwait(const sigset_t * set, siginfo_t * info, |
| struct timespec *timeout) |
| { |
| int ret; |
| int err = sigwait(set, &ret); |
| |
| if (err == 0) |
| return ret; |
| errno = err; |
| return -1; |
| } |
| #endif |
| |
| #ifdef TEST_SIGWAITINFO |
| static int my_sigwaitinfo(const sigset_t * set, siginfo_t * info, |
| struct timespec *timeout) |
| { |
| |
| return sigwaitinfo(set, info); |
| } |
| #endif |
| |
| #ifdef TEST_SIGTIMEDWAIT |
| static int my_sigtimedwait(const sigset_t * set, siginfo_t * info, |
| struct timespec *timeout) |
| { |
| |
| return sigtimedwait(set, info, timeout); |
| } |
| #endif |
| |
| #ifdef TEST_RT_SIGTIMEDWAIT |
| static int my_rt_sigtimedwait(const sigset_t * set, siginfo_t * info, |
| struct timespec *timeout) |
| { |
| |
| /* The last argument is (number_of_signals)/(bits_per_byte), which are 64 and 8, resp. */ |
| return ltp_syscall(__NR_rt_sigtimedwait, set, info, timeout, 8); |
| } |
| #endif |
| |
| void test_empty_set(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs; |
| siginfo_t si; |
| pid_t child; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| /* Run a child that will wake us up */ |
| child = create_sig_proc(100000, signo, UINT_MAX); |
| |
| TEST(sigwaitinfo(&sigs, &si, NULL)); |
| REPORT_SUCCESS(-1, EINTR); |
| |
| kill(child, SIGTERM); |
| } |
| |
| void test_timeout(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs; |
| siginfo_t si; |
| pid_t child; |
| struct timespec ts = {.tv_sec = 1 }; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| |
| /* Run a child that will wake us up */ |
| child = create_sig_proc(100000, signo, UINT_MAX); |
| |
| TEST(sigwaitinfo(&sigs, &si, &ts)); |
| REPORT_SUCCESS(-1, EAGAIN); |
| |
| kill(child, SIGTERM); |
| } |
| |
| /* Note: sigwait-ing for a signal that is not blocked is unspecified |
| * by POSIX; but works for non-ignored signals under Linux |
| */ |
| void test_unmasked_matching(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs; |
| siginfo_t si; |
| pid_t child; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, signo); |
| |
| /* Run a child that will wake us up */ |
| child = create_sig_proc(100000, signo, UINT_MAX); |
| |
| TEST(sigwaitinfo(&sigs, &si, NULL)); |
| REPORT_SUCCESS_COND(signo, 0, si.si_pid == child |
| && si.si_code == SI_USER |
| && si.si_signo == signo, "Struct siginfo mismatch"); |
| |
| kill(child, SIGTERM); |
| } |
| |
| void test_unmasked_matching_noinfo(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs; |
| pid_t child; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, signo); |
| /* Run a child that will wake us up */ |
| child = create_sig_proc(100000, signo, UINT_MAX); |
| |
| TEST(sigwaitinfo(&sigs, NULL, NULL)); |
| REPORT_SUCCESS(signo, 0); |
| |
| kill(child, SIGTERM); |
| } |
| |
| void test_masked_matching(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs, oldmask; |
| siginfo_t si; |
| pid_t child; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, signo); |
| /* let's not get interrupted by our dying child */ |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, SIGCHLD); |
| |
| SUCCEED_OR_DIE(sigprocmask, "sigprocmask failed", SIG_SETMASK, &sigs, |
| &oldmask); |
| |
| /* don't wait on a SIGCHLD */ |
| SUCCEED_OR_DIE(sigdelset, "sigaddset failed", &sigs, SIGCHLD); |
| |
| /* Run a child that will wake us up */ |
| child = create_sig_proc(0, signo, 1); |
| |
| TEST(sigwaitinfo(&sigs, &si, NULL)); |
| REPORT_SUCCESS_COND(signo, 0, si.si_pid == child |
| && si.si_code == SI_USER |
| && si.si_signo == signo, "Struct siginfo mismatch"); |
| |
| SUCCEED_OR_DIE(sigprocmask, "restoring original signal mask failed", |
| SIG_SETMASK, &oldmask, &oldmask); |
| |
| Tst_count--; |
| |
| if (sigismember(&oldmask, signo)) |
| tst_resm(TPASS, "sigwaitinfo restored the original mask"); |
| else |
| tst_resm(TFAIL, |
| "sigwaitinfo failed to restore the original mask"); |
| } |
| |
| void test_masked_matching_rt(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs, oldmask; |
| siginfo_t si; |
| pid_t child[2]; |
| |
| signo = SIGRTMIN + 1; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, signo); |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, signo + 1); |
| /* let's not get interrupted by our dying child */ |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, SIGCHLD); |
| |
| SUCCEED_OR_DIE(sigprocmask, "sigprocmask failed", SIG_SETMASK, &sigs, |
| &oldmask); |
| |
| /* don't wait on a SIGCHLD */ |
| SUCCEED_OR_DIE(sigdelset, "sigdelset failed", &sigs, SIGCHLD); |
| |
| /* Run a child that will wake us up */ |
| child[0] = create_sig_proc(0, signo, 1); |
| child[1] = create_sig_proc(0, signo + 1, 1); |
| |
| TEST(sigwaitinfo(&sigs, &si, NULL)); |
| REPORT_SUCCESS_COND(signo, 0, si.si_pid == child[0] |
| && si.si_code == SI_USER |
| && si.si_signo == signo, "Struct siginfo mismatch"); |
| |
| /* eat the other signal */ |
| Tst_count--; |
| TEST(sigwaitinfo(&sigs, &si, NULL)); |
| REPORT_SUCCESS_COND(signo + 1, 0, si.si_pid == child[1] |
| && si.si_code == SI_USER |
| && si.si_signo == signo + 1, |
| "Struct siginfo mismatch"); |
| |
| SUCCEED_OR_DIE(sigprocmask, "restoring original signal mask failed", |
| SIG_SETMASK, &oldmask, &oldmask); |
| |
| Tst_count--; |
| |
| if (sigismember(&oldmask, signo)) |
| tst_resm(TPASS, "sigwaitinfo restored the original mask"); |
| else |
| tst_resm(TFAIL, |
| "sigwaitinfo failed to restore the original mask"); |
| } |
| |
| void test_masked_matching_noinfo(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs, oldmask; |
| pid_t child; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, signo); |
| /* let's not get interrupted by our dying child */ |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, SIGCHLD); |
| |
| SUCCEED_OR_DIE(sigprocmask, "sigprocmask failed", SIG_SETMASK, &sigs, |
| &oldmask); |
| |
| /* don't wait on a SIGCHLD */ |
| SUCCEED_OR_DIE(sigdelset, "sigaddset failed", &sigs, SIGCHLD); |
| |
| /* Run a child that will wake us up */ |
| child = create_sig_proc(0, signo, 1); |
| |
| TEST(sigwaitinfo(&sigs, NULL, NULL)); |
| REPORT_SUCCESS(signo, 0); |
| |
| SUCCEED_OR_DIE(sigprocmask, "restoring original signal mask failed", |
| SIG_SETMASK, &oldmask, &oldmask); |
| |
| Tst_count--; |
| |
| if (sigismember(&oldmask, signo)) |
| tst_resm(TPASS, "sigwaitinfo restored the original mask"); |
| else |
| tst_resm(TFAIL, |
| "sigwaitinfo failed to restore the original mask"); |
| |
| } |
| |
| void test_bad_address(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs, oldmask; |
| pid_t child; |
| |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, signo); |
| /* let's not get interrupted by our dying child */ |
| SUCCEED_OR_DIE(sigaddset, "sigaddset failed", &sigs, SIGCHLD); |
| |
| SUCCEED_OR_DIE(sigprocmask, "sigprocmask failed", SIG_SETMASK, &sigs, |
| &oldmask); |
| |
| /* don't wait on a SIGCHLD */ |
| SUCCEED_OR_DIE(sigdelset, "sigaddset failed", &sigs, SIGCHLD); |
| |
| /* Run a child that will wake us up */ |
| child = create_sig_proc(0, signo, 1); |
| |
| TEST(sigwaitinfo(&sigs, (void *)1, NULL)); |
| REPORT_SUCCESS(-1, EFAULT); |
| |
| SUCCEED_OR_DIE(sigprocmask, "sigprocmask failed", SIG_SETMASK, &oldmask, |
| &oldmask); |
| |
| kill(child, SIGTERM); |
| } |
| |
| void test_bad_address2(swi_func sigwaitinfo, int signo) |
| { |
| TEST(sigwaitinfo((void *)1, NULL, NULL)); |
| REPORT_SUCCESS(-1, EFAULT); |
| } |
| |
| void test_bad_address3(swi_func sigwaitinfo, int signo) |
| { |
| sigset_t sigs; |
| SUCCEED_OR_DIE(sigemptyset, "sigemptyset failed", &sigs); |
| |
| TEST(sigwaitinfo(&sigs, NULL, (void *)1)); |
| REPORT_SUCCESS(-1, EFAULT); |
| } |
| |
| struct test_desc { |
| test_func tf; |
| swi_func swi; |
| int signo; |
| } tests[] = { |
| #ifdef TEST_RT_SIGTIMEDWAIT |
| { |
| test_empty_set, my_rt_sigtimedwait, SIGUSR1}, { |
| test_unmasked_matching, my_rt_sigtimedwait, SIGUSR1}, { |
| test_masked_matching, my_rt_sigtimedwait, SIGUSR1}, { |
| test_unmasked_matching_noinfo, my_rt_sigtimedwait, SIGUSR1}, { |
| test_masked_matching_noinfo, my_rt_sigtimedwait, SIGUSR1}, { |
| test_bad_address, my_rt_sigtimedwait, SIGUSR1}, { |
| test_bad_address2, my_rt_sigtimedwait, SIGUSR1}, { |
| test_bad_address3, my_rt_sigtimedwait, SIGUSR1}, { |
| test_timeout, my_rt_sigtimedwait, 0}, |
| /* Special cases */ |
| /* 1: sigwaitinfo does respond to ignored signal */ |
| { |
| test_masked_matching, my_rt_sigtimedwait, SIGUSR2}, |
| /* 2: An ignored signal doesn't cause sigwaitinfo to return EINTR */ |
| { |
| test_timeout, my_rt_sigtimedwait, SIGUSR2}, |
| /* 3: The handler is not called when the signal is waited for by sigwaitinfo */ |
| { |
| test_masked_matching, my_rt_sigtimedwait, SIGTERM}, |
| /* 4: Simultaneous realtime signals are delivered in the order of increasing signal number */ |
| { |
| test_masked_matching_rt, my_rt_sigtimedwait, -1}, |
| #endif |
| #if defined TEST_SIGWAIT |
| { |
| test_unmasked_matching_noinfo, my_sigwait, SIGUSR1}, { |
| test_masked_matching_noinfo, my_sigwait, SIGUSR1}, |
| #endif |
| #if defined TEST_SIGWAITINFO |
| { |
| test_empty_set, my_sigwaitinfo, SIGUSR1}, { |
| test_unmasked_matching, my_sigwaitinfo, SIGUSR1}, { |
| test_masked_matching, my_sigwaitinfo, SIGUSR1}, { |
| test_unmasked_matching_noinfo, my_sigwaitinfo, SIGUSR1}, { |
| test_masked_matching_noinfo, my_sigwaitinfo, SIGUSR1}, { |
| test_bad_address, my_sigwaitinfo, SIGUSR1}, { |
| test_bad_address2, my_sigwaitinfo, SIGUSR1}, |
| #endif |
| #if defined TEST_SIGTIMEDWAIT |
| { |
| test_empty_set, my_sigtimedwait, SIGUSR1}, { |
| test_unmasked_matching, my_sigtimedwait, SIGUSR1}, { |
| test_masked_matching, my_sigtimedwait, SIGUSR1}, { |
| test_unmasked_matching_noinfo, my_sigtimedwait, SIGUSR1}, { |
| test_masked_matching_noinfo, my_sigtimedwait, SIGUSR1}, { |
| test_bad_address, my_sigtimedwait, SIGUSR1}, { |
| test_bad_address2, my_sigtimedwait, SIGUSR1}, { |
| test_bad_address3, my_sigtimedwait, SIGUSR1}, { |
| test_timeout, my_sigtimedwait, 0}, |
| #endif |
| }; |
| |
| #if defined TEST_SIGWAITINFO |
| const char *TCID = "sigwaitinfo01"; |
| #elif defined TEST_RT_SIGTIMEDWAIT |
| const char *TCID = "rt_sigtimedwait01"; |
| #elif defined TEST_SIGTIMEDWAIT |
| const char *TCID = "sigtimedwait01"; |
| #elif defined TEST_SIGWAIT |
| const char *TCID = "sigwait01"; |
| #endif |
| |
| int TST_TOTAL = sizeof(tests) / sizeof(*tests); |
| |
| int main(int argc, char **argv) |
| { |
| unsigned i; |
| int lc; |
| char *msg; |
| |
| if ((msg = parse_opts(argc, argv, NULL, NULL)) != NULL) |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| setup(); |
| |
| for (lc = 0; TEST_LOOPING(lc); ++lc) { |
| Tst_count = 0; |
| |
| for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) { |
| alarm(10); /* arrange a 10 second timeout */ |
| tst_resm(TINFO, "%p, %d", tests[i].swi, tests[i].signo); |
| tests[i].tf(tests[i].swi, tests[i].signo); |
| } |
| alarm(0); |
| } |
| |
| cleanup(); |
| tst_exit(); |
| } |