859 lines
26 KiB
C
859 lines
26 KiB
C
|
/*
|
|||
|
* 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., 59
|
|||
|
* Temple Place - Suite 330, Boston MA 02111-1307, 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$ */
|
|||
|
/***********************************************************************************
|
|||
|
|
|||
|
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 <sys/param.h>
|
|||
|
#include <signal.h>
|
|||
|
#include <errno.h>
|
|||
|
#include <fcntl.h>
|
|||
|
#include <string.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include "test.h"
|
|||
|
#include "usctest.h"
|
|||
|
|
|||
|
#define MAXMESG 150 /*The maximum message that can be created. */
|
|||
|
#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. */
|
|||
|
#define SYS_FAIL "The system call %s failed. Errno: %d, Error message: %s."
|
|||
|
|
|||
|
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. */
|
|||
|
extern int errno; /*Error number returned by a system call. */
|
|||
|
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"; /* Test program identifier. */
|
|||
|
int TST_TOTAL=2; /* Total number of test cases. */
|
|||
|
extern int Tst_count; /* Test Case counter for tst_* routines */
|
|||
|
extern int Tst_nobuf; /* var. used to turn off tst_res buffering */
|
|||
|
|
|||
|
int exp_enos[]={0}; /* Array of expected errnos */
|
|||
|
|
|||
|
/***********************************************************************
|
|||
|
* MAIN
|
|||
|
***********************************************************************/
|
|||
|
int
|
|||
|
main(int ac, char **av)
|
|||
|
{
|
|||
|
char mesg[MAXMESG]; /* Holds messages to pass to tst_res. */
|
|||
|
|
|||
|
int lc; /* loop counter */
|
|||
|
const char *msg; /* message returned from parse_opts */
|
|||
|
|
|||
|
Tst_nobuf=1;
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
* parse standard options, and exit if there is an error
|
|||
|
* the -t and -f options not support yet.
|
|||
|
***************************************************************/
|
|||
|
if ( (msg=parse_opts(ac, av, (option_t *) NULL, NULL)) != (char *) NULL ) {
|
|||
|
tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
|
|||
|
tst_exit();
|
|||
|
}
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
* perform global setup for test
|
|||
|
***************************************************************/
|
|||
|
setup();
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
* check looping state if -c option given
|
|||
|
***************************************************************/
|
|||
|
for (lc=0; TEST_LOOPING(lc); lc++) {
|
|||
|
|
|||
|
/* reset Tst_count in case we are looping. */
|
|||
|
Tst_count=0;
|
|||
|
|
|||
|
if ((pid1 = fork()) > 0)
|
|||
|
{
|
|||
|
/*
|
|||
|
* This is the parent, fork again to create child 2.
|
|||
|
*/
|
|||
|
if ((pid2 = fork()) > 0) {
|
|||
|
/*
|
|||
|
* This is the parent.
|
|||
|
*/
|
|||
|
(void) parent_rout();
|
|||
|
}
|
|||
|
else if (pid2 == 0) {
|
|||
|
/*
|
|||
|
* This is child 2.
|
|||
|
*/
|
|||
|
(void) child2_rout();
|
|||
|
}
|
|||
|
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.");
|
|||
|
}
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"fork",errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,cleanup,mesg);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
else if (pid1 == 0) {
|
|||
|
/*
|
|||
|
* This is child 1.
|
|||
|
*/
|
|||
|
(void) child1_rout();
|
|||
|
}
|
|||
|
else {
|
|||
|
/*
|
|||
|
* Fork failed.
|
|||
|
*/
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"fork",errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,cleanup,mesg);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
cleanup();
|
|||
|
|
|||
|
return 0;
|
|||
|
} /* 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()
|
|||
|
{
|
|||
|
char mesg[MAXMESG]; /*Used to buffer messages for tst_resm. */
|
|||
|
|
|||
|
/*
|
|||
|
* 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.");
|
|||
|
tst_exit();
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* 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)
|
|||
|
{
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"kill",errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,NULL,mesg);
|
|||
|
(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.
|
|||
|
*/
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"read",errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,NULL,mesg);
|
|||
|
(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.
|
|||
|
*/
|
|||
|
if ( STD_FUNCTIONAL_TEST )
|
|||
|
tst_resm(TPASS,
|
|||
|
"The signal was sent to all processes in the process group.");
|
|||
|
else
|
|||
|
Tst_count++;
|
|||
|
}
|
|||
|
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.
|
|||
|
*/
|
|||
|
if ( STD_FUNCTIONAL_TEST )
|
|||
|
tst_resm(TPASS,
|
|||
|
"The signal was not sent to selective processes that were not in the process group.");
|
|||
|
else
|
|||
|
Tst_count++;
|
|||
|
}
|
|||
|
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();
|
|||
|
return;
|
|||
|
} /*End of parent_rout*/
|
|||
|
|
|||
|
/*******************************************************************************
|
|||
|
* This is child 1's routine. It creates children A & B, checks their set up,
|
|||
|
* reports to the parent set up pass/fail info., then waits for
|
|||
|
* the parents signal.
|
|||
|
******************************************************************************/
|
|||
|
void child1_rout()
|
|||
|
{
|
|||
|
char mesg[MAXMESG]; /*Used to buffer messages for tst_resm. */
|
|||
|
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()) > 0) {
|
|||
|
/*
|
|||
|
* This is the parent(child1), fork again to create child B.
|
|||
|
*/
|
|||
|
if ((pidB = fork()) == 0) {
|
|||
|
/* This is child B. */
|
|||
|
(void) childB_rout();
|
|||
|
}
|
|||
|
|
|||
|
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.");
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"fork",errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,NULL,mesg);
|
|||
|
(void) write(pipe2_fd[1],CHAR_SET_FAILED,1);
|
|||
|
exit(0);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
else if (pidA == 0) {
|
|||
|
/* This is child A. */
|
|||
|
(void) childA_rout();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
else if (pidA == -1) {
|
|||
|
/*
|
|||
|
* The fork of child A failed.
|
|||
|
*/
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"fork",errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,NULL,mesg);
|
|||
|
(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()
|
|||
|
{
|
|||
|
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()
|
|||
|
{
|
|||
|
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*/
|
|||
|
|
|||
|
/*******************************************************************************
|
|||
|
* This is the routine for child B, which should not receive the parents signal.
|
|||
|
******************************************************************************/
|
|||
|
void childB_rout()
|
|||
|
{
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
/*******************************************************************************
|
|||
|
* This routine sets up the interprocess communication pipes, signal handling,
|
|||
|
* and process group information.
|
|||
|
******************************************************************************/
|
|||
|
void
|
|||
|
setup()
|
|||
|
{
|
|||
|
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. */
|
|||
|
char mesg[MAXMESG]; /*Used to buffer messages for tst_res. */
|
|||
|
|
|||
|
/*
|
|||
|
* Set the process group ID to be equal between the parent and children.
|
|||
|
*/
|
|||
|
(void) setpgrp();
|
|||
|
|
|||
|
/*
|
|||
|
* Set to catch unexpected signals.
|
|||
|
* SIGCLD 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.
|
|||
|
*/
|
|||
|
/* capture signals */
|
|||
|
tst_sig(FORK, DEF_HANDLER, cleanup);
|
|||
|
|
|||
|
if (signal(SIGUSR1,SIG_IGN) == SIG_ERR) {
|
|||
|
(void) sprintf(mesg,
|
|||
|
"Setting to ignore signal SIGCLD failed. Errno: %d, Error message %s.",
|
|||
|
errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,NULL,mesg);
|
|||
|
tst_exit();
|
|||
|
}
|
|||
|
|
|||
|
if (signal(SIGCLD,SIG_IGN) == SIG_ERR) {
|
|||
|
(void) sprintf(mesg,
|
|||
|
"Setting to ignore signal SIGCLD failed. Errno: %d, Error message %s.",
|
|||
|
errno,strerror(errno));
|
|||
|
tst_brkm(TBROK,NULL,mesg);
|
|||
|
tst_exit();
|
|||
|
}
|
|||
|
|
|||
|
/* Indicate which errnos are expected */
|
|||
|
TEST_EXP_ENOS(exp_enos);
|
|||
|
|
|||
|
/* Pause if that option was specified */
|
|||
|
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)
|
|||
|
{
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"pipe",errno_buf,strerror(errno_buf));
|
|||
|
tst_brkm(TBROK, NULL, mesg);
|
|||
|
tst_exit();
|
|||
|
}
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
/***********************************************************
|
|||
|
* This routine indicates that the process caught SIGUSR1.
|
|||
|
**********************************************************/
|
|||
|
void usr1_rout()
|
|||
|
{
|
|||
|
char mesg[MAXMESG]; /*Used to buffer messages for tst_res. */
|
|||
|
|
|||
|
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:
|
|||
|
sprintf(mesg,"Unexpected value %d for who_am_i in usr1_rout().",who_am_i);
|
|||
|
tst_resm(TWARN,mesg);
|
|||
|
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()
|
|||
|
{
|
|||
|
alarm_flag = TRUE;
|
|||
|
|
|||
|
} /*End of notify_timeout*/
|
|||
|
|
|||
|
/***********************************************************
|
|||
|
* This routine handles the procedure for removing the
|
|||
|
* children forked off during this test.
|
|||
|
**********************************************************/
|
|||
|
void par_kill()
|
|||
|
{
|
|||
|
char mesg[MAXMESG]; /*Used to buffer messages for tst_res. */
|
|||
|
/*
|
|||
|
* Indicate to child1 that it can remove it's children and itself now.
|
|||
|
*/
|
|||
|
if (kill(pid1,SIGUSR2) == -1 && errno != ESRCH)
|
|||
|
{
|
|||
|
(void) sprintf(mesg,SYS_FAIL,"kill",errno,strerror(errno));
|
|||
|
tst_resm(TWARN,mesg);
|
|||
|
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.");
|
|||
|
|
|||
|
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()
|
|||
|
{
|
|||
|
char mesg[MAXMESG]; /*Used to buffer messages for tst_resm. */
|
|||
|
|
|||
|
/*
|
|||
|
* Remove children A & B.
|
|||
|
*/
|
|||
|
if (kill(pidA,SIGKILL) == -1 && errno != ESRCH)
|
|||
|
{
|
|||
|
(void) sprintf(mesg,
|
|||
|
"The system call kill failed. Child 1's(A) child may still be alive. Errno: %d, Error message %s.",
|
|||
|
errno,strerror(errno));
|
|||
|
tst_resm(TWARN,mesg);
|
|||
|
}
|
|||
|
if (kill(pidB,SIGKILL) == -1 && errno != ESRCH)
|
|||
|
{
|
|||
|
(void) sprintf(mesg,
|
|||
|
"The system call kill failed. Child 1's(B) child may still be alive. Errno: %d, Error message %s.",
|
|||
|
errno,strerror(errno));
|
|||
|
tst_resm(TWARN,mesg);
|
|||
|
}
|
|||
|
|
|||
|
exit(0);
|
|||
|
|
|||
|
} /*End of chld1_kill*/
|
|||
|
|
|||
|
/***************************************************************
|
|||
|
* cleanup() - performs all ONE TIME cleanup for this test at
|
|||
|
* completion or premature exit.
|
|||
|
***************************************************************/
|
|||
|
void
|
|||
|
cleanup()
|
|||
|
{
|
|||
|
/*
|
|||
|
* print timing stats if that option was specified.
|
|||
|
* print errno log if that option was specified.
|
|||
|
*/
|
|||
|
TEST_CLEANUP;
|
|||
|
|
|||
|
/* exit with return code appropriate for results */
|
|||
|
tst_exit();
|
|||
|
|
|||
|
} /* End cleanup() */
|
|||
|
|
|||
|
|