/* * 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: kill02.c,v 1.10 2009/08/28 13:20:15 vapier Exp $ */ /*********************************************************************************** OS Test - Silicon Graphics, Inc. TEST IDENTIFIER : kill02 Sending a signal to processes with the same process group ID. PARENT DOCUMENT : kiltds01 Kill System Call. AUTHOR : Dave Baumgartner CO-PILOT : Barrie Kletscher DATE STARTED : 12/30/85 TEST ITEMS 1. Sending a signal to pid of zero sends to all processes whose process group ID is equal to the process group ID as the sender. 2. Sending a signal to pid of zero does not send to processes in another process group. OUTPUT SPECIFICATIONS PASS : kiltcs02 1 PASS The signal was sent to all processes in the process group. kiltcs02 2 PASS The signal was not sent to selective processes that were not in the process group. FAIL : kiltcs02 1 FAIL The signal was not sent to all processes in the process group. kiltcs02 2 FAIL The signal was sent to a process that was not in the process group. BROK : kiltcs02 # BROK System call XXX failed. Errno:X, Error message:XXX. kiltcs02 # BROK Setting to catch unexpected signal %d failed. Errno: %d, Error message %s. kiltcs02 # BROK Setting to ignore signal %d failed. Errno: %d, Error message %s. WARN : kiltcs02 0 WARN Unexpected signal X was caught. SPECIAL PROCEDURAL REQUIREMENTS The program must be linked with tst_res.o. DETAILED DESCRIPTION **Setup** Set up unexpected signal handling. Set up one pipe for each process to be created with no blocking for read. **MAIN** If setup fails exit. Fork 2 children(1 & 2). Wait for set up complete messages from the 1st and 2nd child. Send the signal SIGUSR1 with pid equal to zero. Sleep a reasonable amount of time so that each child has been swapped in to process the signal. Now decide the outcome of the test items by reading from each pipe to find out if the child was interrupted by the signal and wrote to it. Remove the second child. Tell the first child it is time to remove it's child B because the decisions have been made. Exit. **First Child** Set to catch SIGUSR1 with an int_rout1. Set up to handle the message from the parent to remove child B. Fork two children(A & B). Wait for set up complete messages from child A & child B. Send a set up complete message to the parent. Pause until the signal SIGUSR1 comes in from the parent. Pause until the parent says it is time to remove the child. Exit. **Second Child** Set to catch SIGUSR1 with an int_rout2. Set the process group to be something different than the parents. Send a set up complete message to the parent. Pause until killed by parent because this child shouldn't receive signal SIGUSR1. **Child A** Set to catch SIGUSR1 with an int_routA. Send a set up complete message to the parent(First Child). Pause until the signal SIGUSR1 comes in from the parent. Exit. **Child B** Set to catch SIGUSR1 with an int_routB. Set the process group to be something different than the parents(First Child's). Send a set up complete message to the parent. Pause until killed by parent because this child shouldn't receive signal SIGUSR1. **usr1_rout-Used by all children** Write to the appropriate pipe that the signal SIGUSR1 was caught. **usr2_rout** Remove child B. ******************************************************************************/ #include #include #include #include #include #include #include #include "test.h" #define CHAR_SET_FAILED "0" /*Set up failing status transferred through the pipe. */ #define CHAR_SET_PASSED "1" /*Set up passing status transferred through the pipe. */ #define SIG_CAUGHT "2" /*Indicates that the signal SIGUSR1 was caught. */ #define SIG_RECEIVED 1 /*Integer value that indicates that the signal SIGUSR1 */ /*was caught. */ #define SIG_NOT_RECD 0 /*Integer value that indicates that the signal SIGUSR1 */ /*was caught. */ #define INT_SET_FAILED 0 /*Set up failing status transferred through the pipe. */ #define INT_SET_PASSED 1 /*Set up passing status transferred through the pipe. */ #define SLEEP_TIME 10 /*Amount of time the children get to catch the signal */ #define TRUE 40 /*Child exits with this if execution was as */ /*expected. */ #define FALSE 50 /*Child exits with this if it timed out waiting for the */ /*parents signal. */ #define TIMEOUT 60 /*Amount of time given in alarm calls. */ #define CHILD_EXIT(VAR) ((VAR >> 8) & 0377) /*Exit value from the child. */ #define CHILD_SIG(VAR) (VAR & 0377) /*Signal value from the termination of child. */ /*from the parent. */ int pid1; /*Return value from 1st fork. Global so that it can be */ /*used in interrupt handling routines. */ int pid2; /*Return value from 2nd fork. Global so that it can be */ /*used in interrupt handling routines. */ int pidA; /*Return value from 1st fork in child 1. Global so that it */ /*can be used in interrupt handling routines. */ int pidB; /*Return value from 2nd fork in child 1. Global so that it */ /*can be used in interrupt handling routines. */ int pipe1_fd[2]; /*Pipe file descriptors used for communication */ /*between child 1 and the 1st parent. */ int pipe2_fd[2]; /*Pipe file descriptors used for communication */ /*between child 2 and the 1st parent. */ int pipeA_fd[2]; /*Pipe file descriptors used for communication */ /*between child A and the 1st parent. */ int pipeB_fd[2]; /*Pipe file descriptors used for communication */ /*between child B and the 1st parent. */ char pipe_buf[10]; /*Pipe buffer. */ char buf_tmp1[2], buf_tmp2[2]; /*Temp hold for info read into pipe_buf. */ int read1_stat = 0; /*Number of characters read from pipe 1. */ int read2_stat = 0; /*Number of characters read from pipe 2. */ int readA_stat = 0; /*Number of characters read from pipe A. */ int readB_stat = 0; /*Number of characters read from pipe B. */ int alarm_flag = FALSE; /*This flag indicates an alarm time out. */ char who_am_i = '0'; /*This indicates which process is which when using */ /*interrupt routine usr1_rout. */ void notify_timeout(); /*Signal handler that the parent enters if it times out */ /*waiting for the child to indicate its set up status. */ void parent_rout(); /*This is the parents routine. */ void child1_rout(); /*This is child 1's routine. */ void child2_rout(); /*This is child 2's routine. */ void childA_rout(); /*This is child A's routine. */ void childB_rout(); /*This is child B's routine. */ void usr1_rout(); /*This routine is used by all children to indicate that */ /*they have caught signal SIGUSR1. */ void par_kill(); /*This routine is called by the original parent to */ /*remove child 2 and to indicate to child 1 to */ /*remove its children. */ void chld1_kill(); /*This routine is used by child 1 to remove itself and */ /*its children A and B. */ void setup(); void cleanup(); char *TCID = "kill02"; int TST_TOTAL = 2; #ifdef UCLINUX static char *argv0; void childA_rout_uclinux(); void childB_rout_uclinux(); #endif int main(int ac, char **av) { int lc; tst_parse_opts(ac, av, NULL, NULL); #ifdef UCLINUX argv0 = av[0]; maybe_run_child(&childA_rout_uclinux, "nd", 1, &pipeA_fd[1]); maybe_run_child(&childB_rout_uclinux, "nd", 2, &pipeB_fd[1]); maybe_run_child(&child1_rout, "ndddddd", 3, &pipe1_fd[1], &pipe2_fd[1], &pipeA_fd[0], &pipeA_fd[1], &pipeB_fd[0], &pipeB_fd[1]); maybe_run_child(&child2_rout, "nd", 4, &pipe2_fd[1]); #endif setup(); for (lc = 0; TEST_LOOPING(lc); lc++) { tst_count = 0; if ((pid1 = FORK_OR_VFORK()) > 0) { if ((pid2 = FORK_OR_VFORK()) > 0) { (void)parent_rout(); } else if (pid2 == 0) { #ifdef UCLINUX if (self_exec(argv0, "nd", 4, pipe2_fd[1]) < 0) { if (kill(pid1, SIGKILL) == -1 && errno != ESRCH) { tst_resm(TWARN, "Child process may not have been killed."); } tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); } #else (void)child2_rout(); #endif } else { /* * The second fork failed kill the first child. */ if (kill(pid1, SIGKILL) == -1 && errno != ESRCH) { tst_resm(TWARN, "Child process may not have been killed."); } tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); } } else if (pid1 == 0) { /* * This is child 1. */ #ifdef UCLINUX if (self_exec (argv0, "ndddddd", 3, pipe1_fd[1], pipe2_fd[1], pipeA_fd[0], pipeA_fd[1], pipeB_fd[0], pipeB_fd[1]) < 0) { tst_brkm(TBROK | TERRNO, cleanup, "self_exec() failed"); } #else (void)child1_rout(); #endif } else { /* * Fork failed. */ tst_brkm(TBROK | TERRNO, cleanup, "fork failed"); } } cleanup(); tst_exit(); } /* END OF MAIN. */ /****************************************************************************** * This is the parents routine. The parent waits for the children 1 and 2 to * get set up. Then sends the signal and checks the outcome. *********************************************************************************/ void parent_rout(void) { /* * Set to catch the alarm signal SIGALRM. */ if (signal(SIGALRM, notify_timeout) == SIG_ERR) { (void)par_kill(); tst_brkm(TBROK, NULL, "Could not set to catch the parents time out alarm."); } /* * Setting to catch the timeout alarm worked now let the children start up. * Set an alarm which causes a time out on the read pipe loop. * The children will notify the parent that set up is complete * and the pass/fail status of set up. */ (void)alarm(TIMEOUT); while ((read(pipe1_fd[0], pipe_buf, 1) != 1) && (alarm_flag == FALSE)) /*EMPTY*/; strncpy(buf_tmp1, pipe_buf, 1); (void)alarm(TIMEOUT); while ((read(pipe2_fd[0], pipe_buf, 1) != 1) && (alarm_flag == FALSE)) /*EMPTY*/; (void)alarm(0); /*Reset the alarm clock. */ strncpy(buf_tmp2, pipe_buf, 1); /* * Check the alarm flag. */ if (alarm_flag == TRUE) { tst_brkm(TBROK, NULL, "The set up of the children failed by timing out."); (void)par_kill(); cleanup(); } /* * Check to see if either child failed in the set up. */ if ((strncmp(buf_tmp1, CHAR_SET_FAILED, 1) == 0) || (strncmp(buf_tmp2, CHAR_SET_FAILED, 1) == 0)) { /* * Problems were encountered in the set up of one of the children. * The error message has been displayed by the child. */ (void)par_kill(); cleanup(); } /* * Setup passed, now send SIGUSR1 to process id of zero. */ TEST(kill(0, SIGUSR1)); if (TEST_RETURN == -1) { tst_brkm(TBROK | TTERRNO, NULL, "kill() failed"); (void)par_kill(); cleanup(); } /* * Sleep for a while to allow the children to get a chance to * catch the signal. */ (void)sleep(SLEEP_TIME); /* * The signal was sent above and time has run out for child response, * check the outcomes. */ read1_stat = read(pipe1_fd[0], pipe_buf, 1); if (read1_stat == -1 && errno == EAGAIN) read1_stat = 0; read2_stat = read(pipe2_fd[0], pipe_buf, 1); if (read2_stat == -1 && errno == EAGAIN) read2_stat = 0; readA_stat = read(pipeA_fd[0], pipe_buf, 1); if (readA_stat == -1 && errno == EAGAIN) readA_stat = 0; readB_stat = read(pipeB_fd[0], pipe_buf, 1); if (readB_stat == -1 && errno == EAGAIN) readB_stat = 0; if (read1_stat == -1 || read2_stat == -1 || readA_stat == -1 || readB_stat == -1) { /* * The read system call failed. */ tst_brkm(TBROK | TERRNO, NULL, "read() failed"); (void)par_kill(); cleanup(); } /* * Check the processes that were supposed to get the signal. */ if (read1_stat == SIG_RECEIVED) { if (readA_stat == SIG_RECEIVED) { /* * Both processes, 1 and A, that were supposed to receive * the signal did receive the signal. */ tst_resm(TPASS, "The signal was sent to all processes in the process group."); } else { /*Process A didn't receive the signal. */ tst_resm(TFAIL, "Process A did not receive the signal."); } } else { /*Process 1 didn't receive the signal. */ tst_resm(TFAIL, "Process 1 did not receive the signal."); } /* * Check the processes that were not supposed to get the signal. */ if (read2_stat == SIG_NOT_RECD) { if (readB_stat == SIG_NOT_RECD) { /* * Both processes, 2 and B did not receive the signal. */ tst_resm(TPASS, "The signal was not sent to selective processes that were not in the process group."); } else { /*Process B received the signal. */ tst_resm(TFAIL, "Process B received the signal."); } } else { /*Process 2 received the signal. */ tst_resm(TFAIL, "Process 2 received the signal."); } (void)par_kill(); (void)alarm(TIMEOUT); while ((read(pipe1_fd[0], pipe_buf, 1) != 1) && (alarm_flag == FALSE)) strncpy(buf_tmp1, pipe_buf, 1); } /*End of parent_rout */ void child1_rout(void) { who_am_i = '1'; /* * Set to catch the SIGUSR1 with int1_rout. */ if (signal(SIGUSR1, usr1_rout) == SIG_ERR) { tst_brkm(TBROK, NULL, "Could not set to catch the childrens signal."); (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); exit(0); } /* * Create children A & B. */ if ((pidA = FORK_OR_VFORK()) > 0) { /* * This is the parent(child1), fork again to create child B. */ if ((pidB = FORK_OR_VFORK()) == 0) { /* This is child B. */ #ifdef UCLINUX if (self_exec(argv0, "nd", 2, pipeB_fd[1]) < 0) { tst_brkm(TBROK | TERRNO, NULL, "self_exec() failed"); (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); exit(0); } #else (void)childB_rout(); #endif } else if (pidB == -1) { /* * The fork of child B failed kill child A. */ if (kill(pidA, SIGKILL) == -1) tst_resm(TWARN, "Child process may not have been killed."); tst_brkm(TBROK | TERRNO, NULL, "fork failed"); (void)write(pipe2_fd[1], CHAR_SET_FAILED, 1); exit(0); } } else if (pidA == 0) { /* This is child A. */ #ifdef UCLINUX if (self_exec(argv0, "nd", 1, pipeA_fd[1]) < 0) { tst_brkm(TBROK | TERRNO, NULL, "self_exec() failed"); (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); exit(0); } #else (void)childA_rout(); #endif } else if (pidA == -1) { /* * The fork of child A failed. */ tst_brkm(TBROK | TERRNO, NULL, "fork failed"); (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); exit(0); } /* * Set to catch the SIGUSR2 with chld1_kill. */ if (signal(SIGUSR2, chld1_kill) == SIG_ERR) { tst_brkm(TBROK, NULL, "Could not set to catch the parents signal."); (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); (void)chld1_kill(); exit(0); } /* * Set to catch the alarm signal SIGALRM. */ if (signal(SIGALRM, notify_timeout) == SIG_ERR) { tst_brkm(TBROK, NULL, "Could not set to catch the childs time out alarm."); (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); (void)chld1_kill(); exit(0); } /* * Setting to catch the signals worked now let the children start up. * Set an alarm which causes a time out on the pipe read loop. * The children A & B will notify the parent(child1) that set up is complete * and the pass/fail status of set up. */ (void)alarm(TIMEOUT - 40); while ((read(pipeA_fd[0], pipe_buf, 1) != 1) && (alarm_flag == FALSE)) /*EMPTY*/; (void)alarm(TIMEOUT - 40); while ((read(pipeB_fd[0], pipe_buf, 1) != 1) && (alarm_flag == FALSE)) /*EMPTY*/; (void)alarm(0); /*Reset the alarm clock. */ /* * Check the alarm flag. */ if (alarm_flag == TRUE) { tst_brkm(TBROK, NULL, "The set up of the children failed by timing out."); (void)chld1_kill(); (void)write(pipe1_fd[1], CHAR_SET_FAILED, 1); exit(0); } /* * Send a set up complete message to the parent. */ (void)write(pipe1_fd[1], CHAR_SET_PASSED, 1); /* * Pause until the signal SIGUSR1 or SIGUSR2 is sent from the parent. */ (void)pause(); /* * Pause until signal SIGUSR2 is sent from the parent. * This pause will only be executed if SIGUSR2 has not been received yet. */ while (1) { sleep(1); } } /*End of child1_rout */ /******************************************************************************* * This is the routine for child 2, which should not receive the parents signal. ******************************************************************************/ void child2_rout(void) { who_am_i = '2'; /* * Set the process group of this process to be different * than the other processes. */ (void)setpgrp(); /* * Set to catch the SIGUSR1 with usr1_rout. */ if (signal(SIGUSR1, usr1_rout) == SIG_ERR) { tst_brkm(TBROK, cleanup, "Could not set to catch the parents signal."); (void)write(pipe2_fd[1], CHAR_SET_FAILED, 1); exit(0); } /* Send a set up complete message to parent. */ (void)write(pipe2_fd[1], CHAR_SET_PASSED, 1); /* * Pause until killed by the parent or SIGUSR1 is received. */ (void)pause(); } /******************************************************************************* * This is the routine for child A, which should receive the parents signal. ******************************************************************************/ void childA_rout(void) { who_am_i = 'A'; /* Send a set up complete message to parent. */ write(pipeA_fd[1], CHAR_SET_PASSED, 1); /* * Pause until killed by the parent or SIGUSR1 is received. */ (void)pause(); exit(0); } /*End of childA_rout */ #ifdef UCLINUX /******************************************************************************* * This is the routine for child A after self_exec ******************************************************************************/ void childA_rout_uclinux(void) { /* Setup the signal handler again */ if (signal(SIGUSR1, usr1_rout) == SIG_ERR) { tst_brkm(TBROK, NULL, "Could not set to catch the childrens signal."); (void)write(pipeA_fd[1], CHAR_SET_FAILED, 1); exit(0); } childA_rout(); } #endif /******************************************************************************* * This is the routine for child B, which should not receive the parents signal. ******************************************************************************/ void childB_rout(void) { who_am_i = 'B'; /* * Set the process group of this process to be different * than the other processes. */ (void)setpgrp(); /* Send a set up complete message to parent(child 1). */ write(pipeB_fd[1], CHAR_SET_PASSED, 1); /* * Pause until killed by the parent(child 1) or SIGUSR1 is received. */ (void)pause(); exit(0); } #ifdef UCLINUX /******************************************************************************* * This is the routine for child B after self_exec ******************************************************************************/ void childB_rout_uclinux(void) { /* Setup the signal handler again */ if (signal(SIGUSR1, usr1_rout) == SIG_ERR) { tst_brkm(TBROK, NULL, "Could not set to catch the childrens signal."); (void)write(pipeB_fd[1], CHAR_SET_FAILED, 1); exit(0); } childB_rout(); } #endif /******************************************************************************* * This routine sets up the interprocess communication pipes, signal handling, * and process group information. ******************************************************************************/ void setup(void) { int errno_buf; /*indicates the errno if pipe set up fails. */ int err_flag = FALSE; /*Indicates if an error has occurred in pipe set up. */ /* * Set the process group ID to be equal between the parent and children. */ (void)setpgrp(); /* * Set to catch unexpected signals. * SIGCHLD is set to be ignored because we do not wait for termination status. * SIGUSR1 is set to be ignored because this is the signal we are using for * the test and we are not concerned with the parent getting it. */ tst_sig(FORK, DEF_HANDLER, cleanup); if (signal(SIGUSR1, SIG_IGN) == SIG_ERR) { tst_brkm(TBROK | TFAIL, NULL, "signal(SIGUSR1, SIG_IGN) failed"); } if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) { tst_brkm(TBROK | TERRNO, NULL, "signal(SIGCHLD, SIG_IGN) failed"); } TEST_PAUSE; /* * Set up pipe1, pipe2, pipeA, and pipeB. */ if ((pipe(pipe1_fd) == -1) || (fcntl(pipe1_fd[0], F_SETFL, O_NDELAY) == -1)) { errno_buf = errno; err_flag = TRUE; } if ((pipe(pipe2_fd) == -1) || (fcntl(pipe2_fd[0], F_SETFL, O_NDELAY) == -1)) { errno_buf = errno; err_flag = TRUE; } if ((pipe(pipeA_fd) == -1) || (fcntl(pipeA_fd[0], F_SETFL, O_NDELAY) == -1)) { errno_buf = errno; err_flag = TRUE; } if ((pipe(pipeB_fd) == -1) || (fcntl(pipeB_fd[0], F_SETFL, O_NDELAY) == -1)) { errno_buf = errno; err_flag = TRUE; } /* * Check for errors. */ if (err_flag == TRUE) { tst_brkm(TBROK | TERRNO, NULL, "pipe() failed"); } return; } /*********************************************************** * This routine indicates that the process caught SIGUSR1. **********************************************************/ void usr1_rout(void) { switch (who_am_i) { case '1': if (write(pipe1_fd[1], SIG_CAUGHT, 1) == -1) tst_resm(TWARN, "Writing signal catching status failed in child 1."); break; case '2': if (write(pipe2_fd[1], SIG_CAUGHT, 1) == -1) tst_resm(TWARN, "Writing signal catching status failed in child 2."); break; case 'A': if (write(pipeA_fd[1], SIG_CAUGHT, 1) == -1) tst_resm(TWARN, "Writing signal catching status failed in child A."); break; case 'B': if (write(pipeB_fd[1], SIG_CAUGHT, 1) == -1) tst_resm(TWARN, "Writing signal catching status failed in child B."); break; default: tst_resm(TWARN, "Unexpected value %d for who_am_i in usr1_rout()", who_am_i); break; } } /*End of usr1_rout */ /*********************************************************** * This routine handles the timeout alarm in the parent, * which occurs when the child fails to notify the parent * the status of set up. **********************************************************/ void notify_timeout(void) { alarm_flag = TRUE; } /*End of notify_timeout */ /*********************************************************** * This routine handles the procedure for removing the * children forked off during this test. **********************************************************/ void par_kill(void) { int status; /* * Indicate to child1 that it can remove it's children and itself now. */ if (kill(pid1, SIGUSR2) == -1 && errno != ESRCH) { tst_resm(TWARN | TERRNO, "kill() failed"); tst_resm(TWARN, "Child 1 and it's children may still be alive."); } /* * Remove child 2. */ if (kill(pid2, SIGKILL) == -1 && errno != ESRCH) tst_resm(TWARN, "Child2 may still be alive."); wait(&status); return; } /*End of par_kill */ /********************************************************************* * This routine is executed by child 1 when the parent tells it to * remove it's children and itself. ********************************************************************/ void chld1_kill(void) { /* * Remove children A & B. */ if (kill(pidA, SIGKILL) == -1 && errno != ESRCH) tst_resm(TWARN | TERRNO, "kill(%d) failed; child 1's(A) child may still be alive", pidA); (void)write(pipe1_fd[1], CHAR_SET_PASSED, 1); if (kill(pidB, SIGKILL) == -1 && errno != ESRCH) tst_resm(TWARN | TERRNO, "kill(%d) failed; child 1's(B) child may still be alive", pidB); exit(0); } /*End of chld1_kill */ /*************************************************************** * cleanup() - performs all ONE TIME cleanup for this test at * completion or premature exit. ***************************************************************/ void cleanup(void) { }