/* * * 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 */ /* * NAME * sigaction01.c * * DESCRIPTION * Test some features of sigaction (see below for more details) * * ALGORITHM * Use sigaction(2) to set a signal handler for SIGUSR1 with a certain * set of flags, set a global variable indicating the test case, and * finally send the signal to ourselves, causing the signal handler to * run. The signal handler then checks the signal handler to run. The * signal handler then checks certain conditions based on the test case * number. * There are 4 test cases: * * 1) Set SA_RESETHAND and SA_SIGINFO. When the handler runs, * SA_SIGINFO should be set. * * 2) Set SA_RESETHAND. When the handler runs, SIGUSR1 should be * masked (SA_RESETHAND makes sigaction behave as if SA_NODEFER was * not set). * * 3) Same as case #2, but when the handler is established, sa_mask is * set to include SIGUSR1. Ensure that SIGUSR1 is indeed masked even if * SA_RESETHAND is set. * * 4) A signal generated from an interface or condition that does not * provide siginfo (such as pthread_kill(3)) should invoke the handler * with a non-NULL siginfo pointer. * * USAGE: <for command-line> * sigaction01 [-c n] [-f] [-i n] [-I x] [-P x] [-t] * where, -c n : Run n copies concurrently. * -f : Turn off functionality Testing. * -i n : Execute test n times. * -I x : Execute test for x seconds. * -P x : Pause for x seconds between iterations. * -t : Turn on syscall timing. * * HISTORY * 07/2001 Ported by Wayne Boyer * * RESTRICTIONS * NONE */ #include <pthread.h> #include <signal.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include "test.h" void setup(); void cleanup(); char *TCID = "sigaction01"; int TST_TOTAL = 4; volatile sig_atomic_t testcase_no; volatile sig_atomic_t pass; /* * handler() * * A signal handler that understands which test case is currently * being executed and compares the current conditions to the ones it * expects (based on the test case number). */ void handler(int sig, siginfo_t * sip, void *ucp) { struct sigaction oact; int err; sigset_t nmask, omask; /* * Get sigaction setting */ err = sigaction(SIGUSR1, NULL, &oact); if (err == -1) { perror("sigaction"); return; } /* * Get current signal mask */ sigemptyset(&nmask); sigemptyset(&omask); err = sigprocmask(SIG_BLOCK, &nmask, &omask); if (err == -1) { perror("sigprocmask"); tst_resm(TWARN, "sigprocmask() failed"); return; } switch (testcase_no) { case 1: /* * SA_RESETHAND and SA_SIGINFO were set. SA_SIGINFO should * be clear in Linux. In Linux kernel, SA_SIGINFO is not * cleared in psig(). */ if (!(oact.sa_flags & SA_SIGINFO)) { tst_resm(TFAIL, "SA_RESETHAND should not " "cause SA_SIGINFO to be cleared, but it was."); return; } if (sip == NULL) { tst_resm(TFAIL, "siginfo should not be NULL"); return; } tst_resm(TPASS, "SA_RESETHAND did not " "cause SA_SIGINFO to be cleared"); break; case 2: /* * In Linux, SA_RESETHAND doesn't imply SA_NODEFER; sig * should not be masked. The testcase should pass if * SA_NODEFER is not masked, ie. if SA_NODEFER is a member * of the signal list */ if (sigismember(&omask, sig) == 0) { tst_resm(TFAIL, "SA_RESETHAND should cause sig to" "be masked when the handler executes."); return; } tst_resm(TPASS, "SA_RESETHAND was masked when handler " "executed"); break; case 3: /* * SA_RESETHAND implies SA_NODEFER unless sa_mask already * included sig. */ if (!sigismember(&omask, sig)) { tst_resm(TFAIL, "sig should continue to be masked" "because sa_mask originally contained sig."); return; } tst_resm(TPASS, "sig has been masked " "because sa_mask originally contained sig"); break; case 4: /* * A signal generated from a mechanism that does not provide * siginfo should invoke the handler with a non-NULL siginfo * pointer. */ if (sip == NULL) { tst_resm(TFAIL, "siginfo pointer should not be NULL"); return; } tst_resm(TPASS, "siginfo pointer non NULL"); break; default: tst_resm(TFAIL, "invalid test case number: %d", testcase_no); exit(1); } } /* * set_handler() * * Establish a signal handler for SIGUSR1 with the specified flags and * signal to mask while the handler executes. */ int set_handler(int flags, int sig_to_mask) { struct sigaction sa; sa.sa_sigaction = handler; sa.sa_flags = flags; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, sig_to_mask); TEST(sigaction(SIGUSR1, &sa, NULL)); if (TEST_RETURN != 0) { perror("sigaction"); tst_resm(TFAIL, "call failed unexpectedly"); return 1; } return 0; } /* * setup() - performs all ONE TIME setup for this test. */ void setup(void) { TEST_PAUSE; } /* * cleanup() - performs all ONE TIME cleanup for this test at * completion or premature exit. */ void cleanup(void) { } int main(int ac, char **av) { int lc; int i; int test_flags[] = { SA_RESETHAND | SA_SIGINFO, SA_RESETHAND, SA_RESETHAND | SA_SIGINFO, SA_RESETHAND | SA_SIGINFO }; tst_parse_opts(ac, av, NULL, NULL); setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { /* reset tst_count in case we are looping */ tst_count = 0; testcase_no = 0; for (i = 0; i < TST_TOTAL; i++) { if (set_handler(test_flags[i], 0) == 0) { testcase_no++; switch (i) { case 0: /*FALLTHROUGH*/ case 1: (void)kill(getpid(), SIGUSR1); break; case 2: /*FALLTHROUGH*/ case 3: (void) pthread_kill(pthread_self(), SIGUSR1); break; default: tst_brkm(TBROK, cleanup, "illegal case number"); break; } } } } cleanup(); tst_exit(); }