/* $Header: /plroot/cuts/1.0/.RCS/PL/src/tests/sys/RCS/sgrtcs02.c,v 1.6 2001/02/15 17:35:16 nstraz Exp $ */ /* * (c) Copyright Cray Research, Inc. Unpublished Proprietary Information. * All Rights Reserved. */ /***************************************************************************** Unicos Testing - Cray Research, Inc. Mendota Heights, Minnesota TEST IDENTIFIER : sgrtcs02 Releasing a single signal. PARENT DOCUMENT : sgrtds01 sigrelse system call AUTHOR : Bob Clark CO-PILOT : Sherri White DATE STARTED : 10/29/86 TEST ITEMS 1-n A pair of signals is associated with each test item. sigrelse turns on the receipt of one signal while the other is being held. OUTPUT SPECIFICATIONS PASS : 1-n sigrelse released sig1 and held sig2. FAIL : 1-n sigrelse did not release sig1. 1-n sigrelse released sig2. BROK : 1-n BROK message. WARN : 0 WARN message. SPECIAL PROCEDURAL REQUIRMENTS The program must be linked with t_result.o and set_usig.o. DETAILED DESCRIPTION set up pipe for parent/child communications for each signal pair: fork off a child process call parent() or child() with the current signal pair exit parent(): set up for unexpected signals wait for child to send ready message over pipe send sig1 and sig2 to child process wait for child to terminate and check exit and signal values record BROK if the child was killed by a signal if exit value is TPASS, TFAIL or TBROK get message from pipe record message and return else if exit is SIG_CAUGHT then BROK (signal caught before released) else if exit is WRITE_BROK then BROK (write() to pipe failed) else if exit is HANDLE_ERR then BROK (error in child's signal handler) else unexpected exit value - BROK return child(): phase 1: set up to catch sig1 and sig2 (exit SIG_CAUGHT if caught) hold sig1 and sig2 with sighold(). send parent ready message if setup went ok. wait for signals to arrive - timeout if they don't phase 2: release sig1 with sigrelse() and wait for handler to catch it (the handler will record the sigs it catches and exit HANDLE_ERR if an error occurs) check that sig1 was caught and sig2 was not. send appropriate message to parent and exit TPASS, TFAIL or TBROK ***************************************************************************/ #include #include #include #include "test.h" #define TRUE 1 #define FALSE 0 #define CHILD_EXIT(VAL) ((VAL >> 8) & 0377) /* exit value of child process */ #define CHILD_SIG(VAL) (VAL & 0377) /* exit value of child process */ #define TCID "sgrtcs02" #define MAXMESG 150 /* the size of the message string */ #define READY "ready" /* signal to parent that child is set up */ #define TIMEOUT 10 /* time (sec) used in the alarm calls */ /* child exit values */ #define SIG_CAUGHT 8 #define WRITE_BROK 16 #define HANDLE_ERR 32 extern int errno; /* system errno number */ extern int T_count; /* number of items covered (in t_result.c) */ int T_total; /* number of test items (initialized below) */ char *Tcid = TCID; /* test case identifier */ static char mesg[MAXMESG]; /* message buffer for t_result */ static int pid; /* process id of child */ static int pipe_fd[2]; /* file descriptors for pipe */ static int phase; /* flag for phase1 or phase2 of signal handler */ static int sig_caught; /* flag TRUE if signal caught (see wait_a_while()) */ /* array of counters for signals caught by handler() */ static int sig_array[NSIG]; extern void t_result(), tt_exit(); static void parent(); static void child(); static int setup_sigs(); static void handler(); static char *read_pipe(); static int write_pipe(); static int set_timeout(); static void clear_timeout(); static void timeout(); static void wait_a_while(); static void cleanup(); /* define the signal-pair structure */ typedef struct { int sig1; /* signal to be held and released */ int sig2; /* signal to be held only */ } sp_t; /* * M A I N */ main() { /* Initialize a signal-pair array to the signals we want to test. * Both signals are held and the first signal is released. */ static sp_t sp[] = { #if defined(linux) {1,2}, {2,3}, {20,21}, {45,30} #else {1,2}, {2,3}, {20,21}, {30,45}, {53,19}, {64,1} #endif }; int i; extern int pipe_fd[]; void parent(), child(); /* initialize T_total (total number of test items */ T_total = sizeof(sp) / sizeof(sp_t); /* set up pipe for parent/child communications */ if (pipe(pipe_fd) < 0) { (void) sprintf(mesg, "pipe() failed. error:%d %s.", errno, strerror(errno)); /* break all remaining test items */ t_result(Tcid, T_count-T_total, TBROK, mesg); tt_exit(); } /* loop through all of the signal pairs */ for (i = 0; i < T_total; i++) { /* * fork off a child process */ if ((pid = fork()) < 0) { (void) sprintf(mesg, "fork() failed. error:%d %s.", errno, strerror(errno)); t_result(TCID, i+1, TBROK, mesg); continue; } else if (pid > 0) { parent(i+1, sp[i].sig1, sp[i].sig2); } else { child(sp[i].sig1, sp[i].sig2); } } /* endfor */ tt_exit(); } /**************************************************************************** * parent() : wait for "ready" from child, send sig1 and sig2 to child, * wait for child to exit and report what happened. ***************************************************************************/ static void parent(tinum, sig1, sig2) int tinum; /* current test item number */ int sig1, sig2; { int term_stat; /* child return status */ int rv; /* function return value */ int sig; /* current signal number */ char *str; /* string returned from read_pipe() */ char *read_pipe(); /* read message from pipe */ void set_usig(); /* set up for unexpected signals */ void cleanup(); /* makes sure child is gone */ /* PARENT PROCESS - first set up for unexpected signals */ set_usig(FORK, DEF_HANDLER, cleanup); /* wait for "ready" message from child */ if ((str = read_pipe()) == NULL) { /* read_pipe() failed. */ t_result(TCID, tinum, TBROK, mesg); cleanup(); return; } if (strcmp(str, READY) != 0) { /* child setup did not go well */ t_result(TCID, tinum, TBROK, str); cleanup(); return; } /* * send signals to child and see if it holds them */ if (kill(pid, sig1) < 0) { (void) sprintf(mesg, "kill() failed. sig:%d error:%d %s.", sig1, errno, strerror(errno)); t_result(TCID, tinum, TBROK, mesg); cleanup(); return; } if (kill(pid, sig2) < 0) { (void) sprintf(mesg, "kill() failed. sig:%d error:%d %s.", sig2, errno, strerror(errno)); t_result(TCID, tinum, TBROK, mesg); cleanup(); return; } /* * child is now releasing sig1, wait and check exit value */ if (wait(&term_stat) < 0) { (void) sprintf(mesg, "wait() failed. error:%d %s.", errno, strerror(errno)); t_result(TCID, tinum, TBROK, mesg); cleanup(); return; } /* check child's signal exit value */ if ((sig = CHILD_SIG(term_stat)) != 0) { /* the child was zapped by a signal */ (void) sprintf(mesg, "Unexpected signal killed child. sig:%d.", sig); t_result(TCID, tinum, TBROK, mesg); return; } /* get child exit value */ rv = CHILD_EXIT(term_stat); switch (rv) { case TPASS: case TFAIL: case TBROK: /* either PASS or FAIL or BROK: check message on pipe */ if ((str = read_pipe()) == NULL) { /* read_pipe() failed. */ t_result(TCID, tinum, TBROK, mesg); break; } /* call t_result: rv contains result type and str contains the message */ t_result(TCID, tinum, rv, str); break; case SIG_CAUGHT: /* a signal was caught before it was released */ t_result(TCID, tinum, TBROK, "A signal was caught before being released."); break; case WRITE_BROK: /* the write() call failed in child's write_pipe */ t_result(TCID, tinum, TBROK, "write() pipe failed for child."); break; case HANDLE_ERR: /* more than one signal tried to be handled at the same time */ t_result(TCID, tinum, TBROK, "Error occured in signal handler."); break; default: (void) sprintf(mesg, "Unexpected return from child. Exit:%d.", rv); t_result(TCID, tinum, TBROK, mesg); break; } return; } /**************************************************************************** * child() : hold signals, notify parent and wait for parent to send signals. * If none were caught (sighold worked), release sig1 and wait until it * is caught. The test PASSs if sig1 is caught once and sig2 is not * caught at all. ***************************************************************************/ static void child(sig1, sig2) int sig1, sig2; { int rv; /* return value from sighold() and sigrelse() */ int sig; /* signal value */ int exit_val; /* exit value to send to parent */ char note[MAXMESG]; /* message buffer for pipe */ extern int sig_caught; /* when TRUE, causes wait_a_while() to return */ int setup_sigs(), write_pipe(), set_timeout(); void handler(), wait_a_while(), clear_timeout(); phase = 1; /* tell handler that we do not want to catch signals */ /* initialize sig1 and sig2 counters in sig_array[] */ sig_array[sig1] = 0; sig_array[sig2] = 0; /* set note to READY and if an error occurs, overwrite it */ (void) strcpy(note, READY); /* set alarm in case something hangs */ if (set_timeout() < 0) { /* an error occured - put mesg in note and send it back to parent */ (void) strcpy(note, mesg); } else if (setup_sigs(sig1, sig2) < 0) { /* an error occured - put mesg in note and send it back to parent */ (void) strcpy(note, mesg); } else { /* all set up to catch signals, now hold them */ if ((rv = sighold(sig1)) != 0) { /* THEY say sighold ALWAYS returns 0 */ (void) sprintf(note, "sighold did not return 0. rv:%d sig:%d.", rv, sig1); } else if ((rv = sighold(sig2)) != 0) { /* THEY say sighold ALWAYS returns 0 */ (void) sprintf(note, "sighold did not return 0. rv:%d sig:%d.", rv, sig2); } } /* * send note 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 with a * value of SIG_CAUGHT. */ if (write_pipe(note) < 0) { /* * write_pipe() failed. Set exit value to WRITE_BROK to let * parent know what happened */ clear_timeout(); exit(WRITE_BROK); } pause(); /* wait for a signal */ clear_timeout(); /* * if we get to this point, all signals have been held and the * timer has expired. Now what we want to do is release each * signal and see if we catch it. If we catch only sig1, * sigrelse passed, else it failed. */ phase = 2; /* let handler know we are now expecting signals */ #ifdef debug printf("child: PHASE II\n"); #endif /* assume success - overwrite note and exit_val if an error occurs */ exit_val = TPASS; /* * release sig1 only and make sure handler only caught sig1 */ if ((rv = sigrelse(sig1)) != 0) { /* THEY say sigrelse ALWAYS returns 0 */ (void) sprintf(note, "sigrelse did not return 0. rv:%d", rv); } else { /* everything went ok, wait for signal to get caught */ wait_a_while(); } /* * Check sig_array[] for sig1 and sig2 if * we are error free so far. If sig1 was * caught once and sig2 was not caught, * send back a PASS, otherwise FAIL. */ if (exit_val == TPASS) { if (sig_array[sig1] != 1) { (void) sprintf(note, "signal %d caught %d times. (expected 1).", sig1, sig_array[sig1]); exit_val = TFAIL; } else if (sig_array[sig2] != 0) { (void) sprintf(note, "signal %d caught %d times. (expected 0).", sig2, sig_array[sig2]); exit_val = TFAIL; } else { (void) sprintf(note, "sig %d was released and sig %d was held.", sig1, sig2); } } /* send note to parent and exit */ if (write_pipe(note) < 0) { /* * write_pipe() failed. Set exit value to WRITE_BROK to let * parent know what happened */ exit(WRITE_BROK); } exit(exit_val); } /***************************************************************************** * setup_sigs() : set child up to catch two signals. If there is * trouble, write message in mesg and return -1, else return 0. * The signal handler has two functions depending on which phase * of the test we are in. The first section is executed after the * signals have been held (should not ever be used). The second * section is executed after the signal has been released (should * be executed for one signal). ****************************************************************************/ static int setup_sigs(sig1, sig2) int sig1, sig2; { void handler(); /* set up signal handler routine */ if (signal(sig1, handler) == SIG_ERR) { /* set up mesg to send back to parent */ (void) sprintf(mesg, "signal() failed for signal %d. error:%d %s.", sig1, errno, strerror(errno)); return(-1); } if (signal(sig2, handler) == SIG_ERR) { /* set up mesg to send back to parent */ (void) sprintf(mesg, "signal() failed for signal %d. error:%d %s.", sig2, errno, strerror(errno)); return(-1); } return(0); } /***************************************************************************** * handler() : child's interrupt handler for all signals. The phase variable * is set in the child process indicating what action is to be taken. * The phase 1 section will be run if the child process catches a signal * after the signal has been held resulting in a test item BROK. * The parent detects this situation by a child exit value of SIG_CAUGHT. * The phase 2 section will be run if the child process catches a * signal after the signal has been released. The correct number of * signals must be caught in order for a PASS. ****************************************************************************/ static void handler(sig) int sig; /* the signal causing the execution of this handler */ { static int s = 0; /* semaphore so that we don't handle 2 sigs at once */ extern int sig_array[]; /* array of counters for signals */ extern int sig_caught; /* flag telling wait_a_while() to stop waiting */ #ifdef debug printf("child: handler phase%d: caught signal %d.\n", phase, sig); #endif if (phase == 1) { /* exit the child process with a value of SIG_CAUGHT */ exit(SIG_CAUGHT); } else { /* phase 2 (error if s gets incremented twice) */ ++s; if (s > 1) { exit(HANDLE_ERR); } ++sig_array[sig]; sig_caught = TRUE; --s; } } /***************************************************************************** * 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. ****************************************************************************/ static char * read_pipe() { static char buf[MAXMESG]; /* buffer for pipe read */ int set_timeout(); void clear_timeout(); #ifdef debug printf("read_pipe: waiting...\n"); #endif /* set timeout alarm in case the pipe is blocked */ if (set_timeout() < 0) { /* an error occured, message in mesg */ return(NULL); } if (read(pipe_fd[0], buf, MAXMESG) < 0) { (void) sprintf(mesg, "read() pipe failed. error:%d %s.", errno, strerror(errno)); clear_timeout(); return(NULL); } clear_timeout(); #ifdef debug printf("read_pipe: received %s.\n", buf); #endif return(buf); } /***************************************************************************** * write_pipe(msg) : write msg to pipe. If it fails, put message in * mesg and return -1, else return 0. ****************************************************************************/ static int write_pipe(msg) char *msg; /* expected message from pipe */ { #ifdef debug printf("write_pipe: sending %s.\n", msg); #endif if (write(pipe_fd[1], msg, MAXMESG) < 0) { (void) sprintf(mesg, "write() pipe failed. error:%d %s.", errno, strerror(errno)); return(-1); } 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() { void timeout(); if (signal(SIGALRM, timeout) == SIG_ERR) { (void) sprintf(mesg, "signal() failed for signal %d. error:%d %s.", SIGALRM, errno, strerror(errno)); return(-1); } (void) alarm(TIMEOUT); return(0); } /***************************************************************************** * clear_timeout() : turn off the alarm so that SIGALRM will not get sent. ****************************************************************************/ static void clear_timeout() { (void) alarm(0); } /***************************************************************************** * timeout() : this routine is executed when the SIGALRM signal is * caught. It does nothing but return - the read() on the pipe * will fail. ****************************************************************************/ static void timeout() { #ifdef debug printf("timeout: sigalrm caught.\n"); #endif } /***************************************************************************** * wait_a_while() : wait a while (TIMEOUT seconds) before returning. ****************************************************************************/ static void wait_a_while() { long btime, time(); extern int sig_caught; /* TRUE if we are done waiting for sig to be caught */ btime = time((long *) 0); while (time((long *) 0) - btime < (long) TIMEOUT) { if (sig_caught == TRUE) break; } } /***************************************************************************** * cleanup() : attempt to kill child process. ****************************************************************************/ static void cleanup() { if (kill(pid, SIGKILL) < 0) { (void) sprintf(mesg, "kill() failed. Child may not have been killed. error:%d %s.", errno, strerror(errno)); t_result(TCID, 0, TWARN, mesg); } } #ifdef VAX /* * unit test for vax only */ static int sighold(signo) int signo; { return(0); } static int sigrelse(signo) int signo; { return(0); } #endif