| /* |
| * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. |
| * |
| * 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. |
| * |
| * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, |
| * Mountain View, CA 94043, or: |
| * |
| * http://www.sgi.com |
| * |
| * For further information regarding this notice, see: |
| * |
| * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ |
| * |
| */ |
| /* $Id: sighold02.c,v 1.11 2009/08/28 13:53:04 vapier Exp $ */ |
| /***************************************************************************** |
| * OS Test - Silicon Graphics, Inc. Eagan, Minnesota |
| * |
| * TEST IDENTIFIER : sighold02 Holding all signals. |
| * |
| * PARENT DOCUMENT : sghtds01 sighold system call (CRAY X-MP and CRAY-1 only) |
| * |
| * AUTHOR : Bob Clark |
| * |
| * CO-PILOT : Barrie Kletscher |
| * |
| * DATE STARTED : 9/26/86 |
| * |
| * TEST ITEMS |
| * |
| * 1. sighold action to turn off the receipt of all signals was done |
| * without error. |
| * 2. After signals were held, and sent, no signals were trapped. |
| * |
| * SPECIAL PROCEDURAL REQUIRMENTS |
| * |
| * The program must be linked with tst_res.o and parse_opts.o. |
| * |
| * DETAILED DESCRIPTION |
| * |
| * set up pipe for parent/child communications |
| * fork off a child process |
| * |
| * PARENT: |
| * set up for unexpected signals |
| * wait for child to send ready message over pipe |
| * issue a result for sighold |
| * send all catchable signals to child |
| * write to pipe, tell child all signals sent |
| * read pipe to get result from child |
| * wait for child to terminate |
| * |
| * CHILD: |
| * set up to catch all signals |
| * hold signals with sighold() |
| * send parent sighold results via pipe |
| * wait for signals to arrive while reading pipe |
| * write to pipe telling parent which signals were received if any. |
| * |
| ***************************************************************************/ |
| |
| #include <errno.h> |
| #include <signal.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| #include "test.h" |
| |
| /* Needed for NPTL */ |
| #define SIGCANCEL 32 |
| #define SIGTIMER 33 |
| |
| #ifdef _CRAYT3E |
| #define CRAYT3E 1 |
| #else |
| #define CRAYT3E 0 |
| #endif |
| |
| #ifdef sgi |
| #define SGI 1 |
| #else |
| #define SGI 0 |
| #endif |
| |
| #ifdef __linux__ |
| /* glibc2.2 definition needs -D_XOPEN_SOURCE, which breaks other things. */ |
| extern int sighold(int __sig); |
| #endif |
| |
| /* ensure NUMSIGS is defined */ |
| #ifndef NUMSIGS |
| #define NUMSIGS NSIG |
| #endif |
| |
| #define CHILD_EXIT(VAL) ((VAL >> 8) & 0377) /* exit value of child process */ |
| |
| #define MAXMESG 150 /* the size of the message string */ |
| |
| #define TIMEOUT 2 /* time used in the alarm calls as backup */ |
| |
| char *TCID = "sighold02"; |
| int TST_TOTAL = 2; |
| |
| char signals_received[MAXMESG]; |
| int pid; /* process id of child */ |
| int Fds1[2]; /* file descriptors for pipe - child 2 parent */ |
| int Fds2[2]; /* file descriptors for pipe - parent 2 child */ |
| |
| #define PARENTSREADFD Fds1[0] |
| #define CHILDSWRITEFD Fds1[1] |
| |
| #define CHILDSREADFD Fds2[0] |
| #define PARENTSWRITEFD Fds2[1] |
| |
| struct pipe_packet { |
| int result; |
| char mesg[MAXMESG]; |
| struct tblock rtimes; |
| } p_p; |
| |
| void do_child(void); |
| void setup(void); |
| void cleanup(void); |
| static void getout(void); |
| static void timeout(int sig); |
| static int read_pipe(int fd); |
| static int write_pipe(int fd); |
| static int setup_sigs(char *mesg); |
| static void handle_sigs(int sig); |
| static int set_timeout(char *mesg); |
| static void clear_timeout(void); |
| |
| int Timeout = 0; |
| |
| /*********************************************************************** |
| * MAIN |
| ***********************************************************************/ |
| int main(int ac, char **av) |
| { |
| int term_stat; /* child return status */ |
| int sig; /* current signal */ |
| int lc; |
| const char *msg; |
| |
| /*************************************************************** |
| * parse standard options, and exit if there is an error |
| ***************************************************************/ |
| if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) { |
| tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg); |
| |
| } |
| #ifdef UCLINUX |
| maybe_run_child(&do_child, "dd", &CHILDSWRITEFD, &CHILDSREADFD); |
| #endif |
| |
| /*************************************************************** |
| * perform global setup for test |
| ***************************************************************/ |
| setup(); |
| |
| /*************************************************************** |
| * check looping state if -c option given |
| ***************************************************************/ |
| for (lc = 0; TEST_LOOPING(lc); lc++) { |
| |
| tst_count = 0; |
| |
| signals_received[0] = '\0'; |
| |
| /* |
| * fork off a child process |
| */ |
| if ((pid = FORK_OR_VFORK()) < 0) { |
| tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); |
| |
| } else if (pid > 0) { |
| |
| /* PARENT PROCESS - first set up for unexpected signals */ |
| tst_sig(FORK, DEF_HANDLER, getout); |
| |
| /* wait for "ready" message from child */ |
| if (read_pipe(PARENTSREADFD) != 0) { |
| /* read_pipe() failed. */ |
| tst_brkm(TBROK, getout, "%s", p_p.mesg); |
| } |
| |
| if (STD_TIMING_ON) { |
| /* |
| * Insure a running total for multiple loop iterations |
| */ |
| tblock.tb_total += p_p.rtimes.tb_total; |
| if (p_p.rtimes.tb_min < tblock.tb_min) |
| tblock.tb_min = p_p.rtimes.tb_min; |
| if (p_p.rtimes.tb_max > tblock.tb_max) |
| tblock.tb_max = p_p.rtimes.tb_max; |
| tblock.tb_count += p_p.rtimes.tb_count; |
| } |
| |
| /* check for ready message */ |
| if (p_p.result != TPASS) { |
| /* child setup did not go well */ |
| tst_brkm(p_p.result, getout, "%s", p_p.mesg); |
| } else { |
| tst_resm(p_p.result, "%s", p_p.mesg); |
| } |
| |
| /* |
| * send signals to child and see if it holds them |
| */ |
| |
| for (sig = 1; sig < NUMSIGS; sig++) { |
| if ((sig == 41) && !CRAYT3E && !SGI) { |
| sig = 42; /* skip over SIGPEFAILURE for non-CRAYT3E systems */ |
| } |
| if ((sig != SIGCLD) && (sig != SIGKILL) && |
| (sig != SIGALRM) && (sig != SIGSTOP) |
| && (sig != SIGCANCEL) |
| && (sig != SIGTIMER) |
| #ifdef SIGRECOVERY |
| && (sig != SIGRECOVERY) |
| #endif |
| #ifdef SIGRESTART |
| && (sig != SIGRESTART) |
| #endif |
| #ifdef SIGNOBDM |
| && (sig != SIGNOBDM) |
| #endif |
| #ifdef SIGPTINTR |
| && (sig != SIGPTINTR) |
| #endif |
| ) { |
| if (kill(pid, sig) < 0) { |
| tst_brkm(TBROK | TERRNO, NULL, |
| "kill(%d, %d) failed", |
| pid, sig); |
| getout(); |
| } |
| } |
| } |
| |
| /* |
| * Tell child that all signals were sent. |
| */ |
| p_p.result = TPASS; |
| strcpy(p_p.mesg, "All signals were sent"); |
| write_pipe(PARENTSWRITEFD); |
| |
| /* |
| * Get childs reply about received signals. |
| */ |
| |
| if (read_pipe(PARENTSREADFD) < 0) { |
| tst_brkm(TBROK, getout, "%s", p_p.mesg); |
| } |
| |
| tst_resm(p_p.result, "%s", p_p.mesg); |
| |
| /* |
| * wait for child |
| */ |
| if (wait(&term_stat) < 0) { |
| tst_brkm(TBROK, getout, "wait() failed"); |
| } |
| |
| } else { |
| |
| /* |
| * CHILD PROCESS - set up to catch signals. |
| */ |
| |
| #ifdef UCLINUX |
| if (self_exec(av[0], "dd", CHILDSWRITEFD, CHILDSREADFD) |
| < 0) { |
| tst_brkm(TBROK | TERRNO, cleanup, |
| "self_exec() failed"); |
| } |
| #else |
| do_child(); |
| #endif |
| } |
| } |
| cleanup(); |
| tst_exit(); |
| |
| } |
| |
| /***************************************************************************** |
| * do_child() |
| ****************************************************************************/ |
| |
| void do_child(void) |
| { |
| int rv; /* function return value */ |
| int sig; /* current signal */ |
| int cnt; |
| |
| p_p.result = TPASS; |
| |
| /* set up signal handlers for the signals */ |
| if (setup_sigs(p_p.mesg) < 0) { |
| p_p.result = TBROK; |
| |
| } else { |
| /* all set up to catch signals, now hold them */ |
| |
| for (cnt = 0, sig = 1; sig < NUMSIGS; sig++) { |
| if ((sig == 41) && !CRAYT3E && !SGI) { |
| sig = 42; /* skip over SIGPEFAILURE for non-CRAYT3E systems */ |
| } |
| if ((sig != SIGCLD) && (sig != SIGKILL) && |
| (sig != SIGALRM) && (sig != SIGSTOP) |
| #ifdef SIGNOBDM |
| && (sig != SIGNOBDM) |
| #endif |
| && (sig != SIGCANCEL) && (sig != SIGTIMER) |
| ) { |
| |
| cnt++; |
| TEST(sighold(sig)); |
| rv = TEST_RETURN; |
| if (rv != 0) { |
| /* THEY say sighold ALWAYS returns 0 */ |
| p_p.result = TFAIL; |
| (void)sprintf(p_p.mesg, |
| "sighold(%d) failed, rv:%d, errno:%d", |
| sig, rv, errno); |
| break; |
| } |
| } |
| } |
| if (STD_TIMING_ON) { |
| p_p.rtimes = tblock; |
| } |
| if (p_p.result == TPASS) { |
| sprintf(p_p.mesg, |
| "Sighold called without error for %d of %d signals", |
| cnt, NUMSIGS - 1); |
| } |
| } |
| |
| /* |
| * write to parent (if not READY, parent will BROK) and |
| * wait for parent to send signals. The timeout clock is set so |
| * that we will not wait forever - if sighold() did its job, we |
| * will not receive the signals. If sighold() blew it we will |
| * catch a signal and the interrupt handler will exit(1). |
| */ |
| #if debug |
| printf("child: %d writing to parent fd:%d\n", getpid(), CHILDSWRITEFD); |
| #endif |
| if (write_pipe(CHILDSWRITEFD) < 0 || p_p.result != TPASS) { |
| exit(2); |
| } |
| |
| /* |
| * Read pipe from parent, that will tell us that all signals were sent |
| */ |
| if (read_pipe(CHILDSREADFD) != 0) { |
| p_p.result = TBROK; |
| strcpy(p_p.mesg, "read() pipe failed"); |
| } else if (signals_received[0] == '\0') { |
| p_p.result = TPASS; |
| strcpy(p_p.mesg, |
| "No signals trapped after being sent by parent"); |
| } else { |
| p_p.result = TFAIL; |
| sprintf(p_p.mesg, "signals received: %s", signals_received); |
| } |
| |
| if (write_pipe(CHILDSWRITEFD) < 0) { |
| exit(2); |
| } |
| |
| /* exit back to parent */ |
| if (p_p.result == TPASS) |
| exit(0); |
| else |
| exit(1); |
| } |
| |
| /***************************************************************************** |
| * read_pipe() : read data from pipe and return in buf. If an error occurs |
| * put message in mesg and return NULL. Note: this routine sets a |
| * timeout signal in case the pipe is blocked. |
| ****************************************************************************/ |
| |
| int read_pipe(int fd) |
| { |
| int ret = -1; |
| |
| #ifdef debug |
| printf("read_pipe: %d entering, fd = %d...\n", getpid(), fd); |
| #endif |
| |
| /* set timeout alarm in case the pipe is blocked */ |
| if (set_timeout(p_p.mesg) < 0) { |
| /* an error occured, message in mesg */ |
| return -1; |
| } |
| |
| ret = read(fd, (char *)&p_p, sizeof(struct pipe_packet)); |
| |
| #ifdef debug |
| printf("read_pipe: %d read() completed ret=%d\n", getpid(), ret); |
| #endif |
| |
| clear_timeout(); |
| |
| if (Timeout) { |
| (void)sprintf(p_p.mesg, |
| "read() pipe failed -timed out after %d seconds.", |
| TIMEOUT); |
| return -1; |
| } |
| #ifdef debug |
| printf("read_pipe: received %s.\n", p_p.mesg); |
| #endif |
| |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * write_pipe(msg) : write p_p to pipe. If it fails, put message in |
| * mesg and return -1, else return 0. |
| ****************************************************************************/ |
| |
| static int write_pipe(int fd) |
| { |
| #ifdef debug |
| printf("write_pipe: sending result:%d, mesg:%s.\n", p_p.result, |
| p_p.mesg); |
| #endif |
| if (write(fd, (char *)&p_p, sizeof(struct pipe_packet)) < 0) { |
| if (pid) |
| tst_brkm(TBROK | TERRNO, getout, "write() pipe failed"); |
| return -1; |
| } |
| #ifdef debug |
| printf("write_pipe: %d complete successfully, fd:%d.\n", getpid(), fd); |
| #endif |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * set_timeout() : set alarm to signal process after the period of time |
| * indicated by TIMEOUT. If the signal occurs, the routine timeout() |
| * will be executed. If all goes ok, return 0, else load message |
| * into mesg and return -1. |
| ****************************************************************************/ |
| |
| static int set_timeout(char *mesg) |
| { |
| if (signal(SIGALRM, timeout) == SIG_ERR) { |
| (void)sprintf(mesg, |
| "signal() failed for signal %d. error:%d %s.", |
| SIGALRM, errno, strerror(errno)); |
| return (-1); |
| } |
| #if debug |
| printf("set_timeout()...\n"); |
| #endif |
| |
| Timeout = 0; |
| (void)alarm(TIMEOUT); |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * clear_timeout() : turn off the alarm so that SIGALRM will not get sent. |
| ****************************************************************************/ |
| |
| static void clear_timeout(void) |
| { |
| (void)alarm(0); |
| Timeout = 0; |
| } |
| |
| /***************************************************************************** |
| * timeout() : this routine is executed when the SIGALRM signal is |
| * caught. It does nothing but return - if executed during a read() |
| * system call, a -1 will be returned by the read(). |
| ****************************************************************************/ |
| |
| static void timeout(int sig) |
| { |
| #ifdef debug |
| printf("timeout: sigalrm caught.\n"); |
| #endif |
| Timeout = 1; |
| |
| } |
| |
| /***************************************************************************** |
| * setup_sigs() : set child up to catch all signals. If there is |
| * trouble, write message in mesg and return -1, else return 0. |
| ****************************************************************************/ |
| |
| static int setup_sigs(char *mesg) |
| { |
| int sig; |
| |
| /* set up signal handler routine */ |
| for (sig = 1; sig < NUMSIGS; sig++) { |
| if ((sig != SIGCLD) && (sig != SIGKILL) && |
| (sig != SIGALRM) && (sig != SIGSTOP) |
| #ifdef SIGRESTART |
| && (sig != SIGRESTART) |
| #endif |
| #ifdef SIGRECOVERY |
| && (sig != SIGRECOVERY) |
| #endif |
| #ifdef SIGSWAP |
| && (sig != SIGSWAP) |
| #endif |
| && (sig != SIGCANCEL) && (sig != SIGTIMER) |
| ) { |
| |
| if (signal(sig, handle_sigs) == SIG_ERR) { |
| /* set up mesg to send back to parent */ |
| (void)sprintf(mesg, |
| "signal() failed for signal %d. error:%d %s.", |
| sig, errno, strerror(errno)); |
| return (-1); |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /***************************************************************************** |
| * handle_sigs() : interrupt handler for all signals. This will be run |
| * if the child process catches a signal (resulting in |
| * a test item FAIL if tst_res has not yet been called). The parent |
| * detects this situation by a child exit value of 1. |
| ****************************************************************************/ |
| |
| static void handle_sigs(int sig) |
| { |
| char string[10]; |
| |
| #ifdef debug |
| printf("child: handle_sigs: caught signal %d.\n", sig); |
| #endif |
| |
| sprintf(string, " %d", sig); |
| strcat(signals_received, string); |
| |
| return; |
| } |
| |
| /***************************************************************************** |
| * getout() : attempt to kill child process and call cleanup(). |
| ****************************************************************************/ |
| |
| static void getout(void) |
| { |
| if (kill(pid, SIGKILL) < 0) |
| tst_resm(TWARN | TERRNO, "kill(%d) failed", pid); |
| cleanup(); |
| } |
| |
| /*************************************************************** |
| * setup() - performs all ONE TIME setup for this test. |
| ***************************************************************/ |
| void setup(void) |
| { |
| |
| tst_sig(FORK, DEF_HANDLER, cleanup); |
| |
| /* set up pipe for child sending to parent communications */ |
| if (pipe(Fds1) < 0) |
| tst_brkm(TBROK | TERRNO, cleanup, "pipe() failed"); |
| |
| /* set up pipe for parent sending to child communications */ |
| if (pipe(Fds2) < 0) |
| tst_brkm(TBROK | TERRNO, cleanup, "pipe() failed"); |
| |
| #if debug |
| printf("child 2 parent Fds1[0] = %d, Fds1[1] = %d\n", Fds1[0], Fds1[1]); |
| printf("parent 2 child Fds2[0] = %d, Fds2[1] = %d\n", Fds2[0], Fds2[1]); |
| #endif |
| |
| TEST_PAUSE; |
| |
| } |
| |
| /*************************************************************** |
| * cleanup() - performs all ONE TIME cleanup for this test at |
| * completion or premature exit. |
| ***************************************************************/ |
| void cleanup(void) |
| { |
| |
| } |