[BACK]Return to kill10.c CVS log [TXT][DIR] Up to [Development] / ltp / tests

File: [Development] / ltp / tests / kill10.c (download)

Revision 1.1, Tue Apr 17 18:54:56 2001 UTC (16 years, 6 months ago) by nstraz
Branch: MAIN
CVS Tags: HEAD

Adding a new test program that tests signals with lots of processes.  This is
what can happen when you get carried away for a week.  :)  There may be some
race conditions left that will stick MP machines when -g and -n are large.

/*
 * 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: kill10.c,v 1.1 2001/04/17 18:54:56 nstraz Exp $ */
/**********************************************************
 * 
 *    OS Test - Silicon Graphics, Inc.
 * 
 *    TEST IDENTIFIER	: kill10
 * 
 *    EXECUTED BY	: anyone
 * 
 *    TEST TITLE	: signal flooding test
 * 
 *    TEST CASE TOTAL	: 1
 * 
 *    WALL CLOCK TIME	: 
 * 
 *    CPU TYPES		: ALL
 * 
 *    AUTHOR		: Nate Straz
 * 
 *    DATE STARTED	: 04/09/2001
 * 
 *    INITIAL RELEASE	: Linux 2.4.x
 * 
 *    TEST CASES
 * 
 * 	1.) Create a large number of processes and signal between them.
 *	
 *    INPUT SPECIFICATIONS
 * 	The standard options for system call tests are accepted.
 *	(See the parse_opts(3) man page).
 * 
 *    OUTPUT SPECIFICATIONS
 * 	
 *    DURATION
 * 	Terminates - with frequency and infinite modes.
 * 
 *    SIGNALS
 * 	Uses SIGUSR1 to pause before test if option set.
 * 	(See the parse_opts(3) man page).
 *
 *    RESOURCES
 * 	None
 * 
 *    ENVIRONMENTAL NEEDS
 *      No run-time environmental needs.
 * 
 *    SPECIAL PROCEDURAL REQUIREMENTS
 * 	None
 * 
 *    INTERCASE DEPENDENCIES
 * 	None
 * 
 *    DETAILED DESCRIPTION
 *  This test creates -g groups of -n processes each and prepares them to send
 *  large numbers of signals.  All process fall into three levels.
 *    * Level 1 - Master
 *                This is the parent of all processes.  It handles test looping
 *                and making sure that all level 2 Managers report in.
 *                SIGUSR1 -> ack Manager is ready
 *                SIGUSR2 -> ack Manager is done and sends reset
 *    * Level 2 - Managers
 *                There are -g (default 2) of these processes.  They handle
 *                forking off -n procs and setting up their signal handling.
 *                Managers are in a pgid with their Children.
 *                SIGALRM -> Process making your children
 *                SIGUSR1 -> 
 *                SIGUSR2 -> Reply to Child to stop
 *                SIGHUP  -> Reset child signal counter
 *                SIGQUIT -> Exit gracefully
 *    * Level 3 - Child
 *                There are -n (default 10) of these process per Manager.  Their
 *                only job is to send signals to their Managers when told to by
 *                the Master.
 *                SIGUSR1 -> Start signaling Manager
 *                SIGUSR2 -> Stop signaling Manager
 *                SIGHUP  -> IGNORE
 *                SIGQUIT -> Exit gracefully
 *
 *  During each test loop, Master sends SIGUSR1 to the pgid of each Manager.
 *  This tells the Children to start signalling their manager.  They do this
 *  until the manager signals them to stop.  Once the manager finds that all
 *  children have been signaled (by checking them off in the checklist), the
 *  Manager signals the Master.  Once the Master acknowledges that all Managers
 *  have talked to all their Children, the test iteration is over.
 *
 * 	Setup:
 *	  Pause for SIGUSR1 if option specified.
 *	  Fork -g Managers
 *	    Set up signal handling for Children
 *	    Fork -n Children for each manager
 *	    Set up signal handling for Managers
 *	  Set up signal handling for Master
 * 
 * 	Test:
 *	 Loop if the proper options are given.
 *	   Send SIGUSR1 to all Managers and their Children
 *	   Wait for Managers to send SIGUSR2
 * 
 * 	Cleanup:
 * 	  Send SIGQUIT to all Manager process groups and wait for Manager to quit.
 * 	  Print errno log and/or timing stats if options given
 *
 *  Debugging:
 *    0 - normal operations
 *    1 - Master setup
 *    2 - Master processing
 *    3 - Master - Manager interaction
 *    4 - Manager setup
 *    5 - Manager processing
 *    6 - Manager - Child interaction
 *    7 - Child setup
 *    8 - Child processing
 * 
 * 
 *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/

#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include "test.h"
#include "usctest.h"

void setup(); 
void help();
void cleanup();
void fork_pgrps(int pgrps_left);
void manager(int num_procs);
void fork_procs(int procs_left);

/* signal handlers */
void ack_ready(int sig, siginfo_t *si, void *data);
void ack_done(int sig, siginfo_t *si, void *data);
void set_create_procs(int sig); 
void graceful_exit(int sig);
void set_signal_parents(int sig);
void clear_signal_parents(int sig);
void set_confirmed_ready(int sig);
void reset_counter(int sig);
void reply_to_child(int sig, siginfo_t *si, void *data);
void wakeup(int sig);

/* pid checklist management */
struct pid_list_item {
    pid_t pid;
    short flag;
} *child_checklist = NULL;
int child_checklist_total = 0;
int checklist_cmp(const void *a, const void *b);
void checklist_reset(int bit);

inline int k_sigaction(int sig, struct sigaction *sa, struct sigaction *osa);

char *TCID="kill10";		/* Test program identifier.    */
int TST_TOTAL=1;    		/* Total number of test cases. */
extern int Tst_count;		/* Test Case counter for tst_* routines */
extern int Tst_nobuf;

int exp_enos[]={0, 0};

int num_procs = 10;
int num_pgrps = 2;
int pgrps_ready = 0;
int child_signal_counter = 0;

int create_procs_flag = 0;
int signal_parents_flag = 0;
int confirmed_ready_flag = 0;
int debug_flag = 0;
pid_t mypid = 0;

char *narg, *garg, *darg;
int nflag=0, gflag=0, dflag=0;

/* for test specific parse_opts options */
option_t options[] = {
        { "n:",  &nflag, &narg },       /* -n #procs */
        { "g:",  &gflag, &garg },       /* -g #pgrps */
        { "d:",  &dflag, &darg },       /* -d <debug level>  */
        { NULL, NULL, NULL }
};

/***********************************************************************
 * Main
 ***********************************************************************/
int
main(int ac, char **av)
{
    int lc;	        /* loop counter */
    char *msg;      /* message returned from parse_opts */
    int cnt;

    Tst_nobuf=1;

    /***************************************************************
     * parse standard options
     ***************************************************************/
    /* start off by parsing the command line options.  We provide a function
     * that understands many common options to control looping.  If you are not
     * adding any new options, pass NULL in place of options and &help.
     */
    if ((msg = parse_opts(ac, av, options, &help))) {
        tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
        tst_exit();
    }

    if (nflag) {
        if (sscanf(narg, "%i", &num_procs) != 1 ) {
            tst_brkm(TBROK, NULL, "-n option arg is not a number");
            tst_exit();
        }
    }
    if (gflag) {
        if (sscanf(garg, "%i", &num_pgrps) != 1 ) {
            tst_brkm(TBROK, NULL, "-g option arg is not a number");
            tst_exit();
        }
    }

    if (dflag) {
        if (sscanf(darg, "%i", &debug_flag) != 1 ) {
            tst_brkm(TBROK, NULL, "-d option arg is not a number");
            tst_exit();
        }
    }

    /***************************************************************
     * perform global setup for test
     ***************************************************************/
    /* Next you should run a setup routine to make sure your environment is
     * sane.
     */
    setup();

    /* set the expected errnos... */
    TEST_EXP_ENOS(exp_enos);

    /***************************************************************
     * check looping state 
     ***************************************************************/
    /* TEST_LOOPING() is a macro that will make sure the test continues
     * looping according to the standard command line args. 
     */
    for (lc=0; TEST_LOOPING(lc); lc++) {

        /* reset Tst_count in case we are looping. */
        Tst_count=0;
        child_signal_counter = 0;
        pgrps_ready = 0;
        checklist_reset(0x03);

        /* send SIGUSR1 to each pgroup */
        for (cnt = 0; cnt < child_checklist_total; ++cnt) {
            if (debug_flag  >= 2)
                printf("%d: test_loop, SIGUSR1 -> %d\n", 
                                mypid, -child_checklist[cnt].pid);
            kill(-child_checklist[cnt].pid, SIGUSR1);
        }

        /* wait for the managers to signal they are done */
        while (child_signal_counter < num_pgrps) {
            alarm(1);
            if (debug_flag  >= 2) 
                printf("%d: Master pausing for done (%d/%d)\n", 
                                mypid, child_signal_counter, num_pgrps);
            pause();
        }
        tst_resm(TPASS, "All %d pgrps received their signals", child_signal_counter);

	/* Here we clean up after the test case so we can do another iteration.
	 */

    }	/* End for TEST_LOOPING */

    /***************************************************************
     * cleanup and exit
     ***************************************************************/
    cleanup();

    return 0;
}	/* End main */

/***************************************************************
 * help
 ***************************************************************/
/* The custom help() function is really simple.  Just write your help message to
 * standard out.  Your help function will be called after the standard options
 * have been printed
 */
void
help()
{
	printf("  -g n    Create n process groups (default: %d)\n", num_pgrps);
    printf("  -n n    Create n children in each process group (default: %d)\n", num_procs);
    printf("  -d n    Set debug level to n (default: %d)\n", debug_flag);
}

/***************************************************************
 * setup() - performs all ONE TIME setup for this test.
 ***************************************************************/
void 
setup()
{
    struct sigaction sa;
    int i;

    /* You will want to enable some signal handling so you can capture
     * unexpected signals like SIGSEGV. 
     */
    tst_sig(FORK, DEF_HANDLER, cleanup);


    /* Pause if that option was specified */
    /* One cavet that hasn't been fixed yet.  TEST_PAUSE contains the code to
     * fork the test with the -c option.  You want to make sure you do this
     * before you create your temporary directory.
     */
    TEST_PAUSE;

    mypid = getpid();
    sa.sa_handler = SIG_DFL;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (debug_flag >= 1)
        printf("%d: setting SIGTRAP -> SIG_DFL\n", mypid);
    k_sigaction(SIGTRAP, &sa, 0);
    if (debug_flag >= 1)
        printf("%d: setting SIGCONT -> SIG_DFL\n", mypid);
    k_sigaction(SIGCONT, &sa, 0);

    sa.sa_handler = set_create_procs;
    if (debug_flag >= 4)
        printf("%d: setting SIGALRM -> set_create_procs\n", mypid);
    k_sigaction(SIGALRM, &sa, 0);

    sa.sa_handler = NULL;
    sa.sa_sigaction = ack_ready;
    sa.sa_flags = SA_SIGINFO;
    if (debug_flag >= 1)
        printf("%d: setting SIGUSR1 -> ack_ready\n", mypid);
    k_sigaction(SIGUSR1, &sa, 0);

    fork_pgrps(num_pgrps);

    /* wait for all pgrps to report in */
    printf("Master: %d\n", mypid);
    while (pgrps_ready < num_pgrps) { 
        if (debug_flag  >= 3) 
            printf("%d: Master pausing for Managers to check in (%d/%d)\n", 
                            mypid, pgrps_ready, num_pgrps);
        pause(); 
    }
    checklist_reset(0x03);
    printf("Managers: \n");
    for (i = 0; i < num_pgrps; i++) { printf("%d ", child_checklist[i].pid); }
    printf("\n");

    /* set up my signal processing */
    /* continue on ALRM */
    sa.sa_handler = wakeup;
    if (debug_flag >= 4)
        printf("%d: setting SIGALRM -> wakeup\n", mypid);
    k_sigaction(SIGALRM, &sa, 0);
    /* reply to child on USR2 */
    sa.sa_handler = NULL;
    sa.sa_sigaction = ack_done;
    sa.sa_flags = SA_SIGINFO;
    if (debug_flag  >= 1)
        printf("%d: setting SIGUSR2 -> ack_done\n", mypid);
    k_sigaction(SIGUSR2, &sa, 0);
}

void 
ack_ready(int sig, siginfo_t *si, void *data)
{
    struct pid_list_item findit, *result;

    findit.pid = si->si_pid;

    result = bsearch(&findit, child_checklist, child_checklist_total, 
                    sizeof(*child_checklist), checklist_cmp);
    if (result) {
        if (!(result->flag & 0x01)) {
            if (debug_flag >= 3)
                printf("%d: ack_ready, SIGUSR1 -> %d\n", mypid, si->si_pid);
            kill(si->si_pid, SIGUSR1);
            result->flag = result->flag | 0x01;
            ++pgrps_ready;
        } else {
            if (debug_flag >= 3)
                printf("%d: ack_ready, already acked %d\n", mypid, si->si_pid);
        }
    } else {
        tst_brkm(TBROK, cleanup,
                        "received unexpected signal %d from %d", sig, si->si_pid);
    }
}

void 
ack_done(int sig, siginfo_t *si, void *data)
{
    struct pid_list_item findit, *result;

    
    findit.pid = si->si_pid;

    result = bsearch(&findit, child_checklist, child_checklist_total, 
                    sizeof(*child_checklist), checklist_cmp);
    if (result) {
        if (!(result->flag & 0x02)) {
            if (debug_flag  >= 3) 
                printf("%d: ack_done, SIGHUP -> %d\n", mypid, si->si_pid);
            kill(si->si_pid, SIGHUP);
            ++child_signal_counter;
            result->flag = result->flag | 0x02;
        } else {
            if (debug_flag >= 3)
                printf("%d: ack_done, already told %d\n", mypid, si->si_pid);
        }
    } else {
        tst_brkm(TBROK, cleanup, 
                        "received unexpected signal from %d", si->si_pid);
    }
}

/***************************************************************
 * cleanup() - performs all ONE TIME cleanup for this test at
 *		completion or premature exit.
 ***************************************************************/
void 
cleanup()
{
    int i;
    /* send SIGHUP to all pgroups */
    for (i = 0; i < num_pgrps; ++i) {
        /* try to do this as nicely as possible */
        kill(-child_checklist[i].pid, SIGQUIT);
        waitpid(child_checklist[i].pid, NULL, 0);
    }
    /*
     * 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();
}

/*********************************************************************
 * fork_pgrps() forks off a child, changes it's pgrp, then continues
 ********************************************************************/
void
fork_pgrps(int pgrps_left)
{
    pid_t child;

    if (!(child_checklist = calloc(pgrps_left, sizeof(*child_checklist)))) {
        tst_brkm(TBROK, cleanup,
                        "%d: couldn't calloc child_checklist, errno=%d : %s",
                        mypid, errno, strerror(errno));
    }
    child_checklist_total = 0;
    while (pgrps_left) {
        if (debug_flag  >= 1)
            printf("%d: forking new Manager\n", mypid);
        switch (child = fork()) {
                case -1: 
                        tst_brkm(TBROK, cleanup,
                                        "fork() failed in fork_pgrps(%d), errno=%d : %s", 
                                        pgrps_left, errno, strerror(errno));
                        break;
                case 0: mypid = getpid();
                        free(child_checklist);
                        child_checklist = NULL;
                        manager(num_procs);
                        break;
                default: 
                        child_checklist[child_checklist_total++].pid = child;
                        setpgid(child, child);
                        if (debug_flag  >= 3)
                            printf("%d: fork_pgrps, SIGALRM -> %d\n", mypid, child);
                        kill(child, SIGALRM);
        }
        --pgrps_left;
    }
    qsort(child_checklist, child_checklist_total, sizeof(*child_checklist), checklist_cmp);
}

void
set_create_procs(int sig)
{
    if (debug_flag  >= 3) 
        printf("%d: Manager cleared to fork\n", getpid());
    create_procs_flag++;
    return;
}

/*********************************************************************
 * new_pgrg() - handle the creation of the pgrp managers and their 
 *              children
 ********************************************************************/
void
manager(int num_procs)
{
    struct sigaction sa;

    /* Wait for the parent to change our pgid before we start forking */
    while (!create_procs_flag) {
        alarm(1);
        if (debug_flag  >= 3)
            printf("%d: Manager pausing, not cleared to fork\n", mypid);
        pause();
    }

    /* set up the signal handling the children will use */

    /* ignore ALRM and HUP */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (debug_flag  >= 4)
        printf("%d: setting SIGALRM -> SIG_IGN\n", mypid);
    k_sigaction(SIGALRM, &sa, 0);
    if (debug_flag  >= 4)
        printf("%d: setting SIGHUP -> SIG_IGN\n", mypid);
    k_sigaction(SIGHUP, &sa, 0);

    /* exit on QUIT */
    sa.sa_handler = graceful_exit;
    if (debug_flag  >= 4)
        printf("%d: setting SIGQUIT -> graceful_exit\n", mypid);
    k_sigaction(SIGQUIT, &sa, 0);

    /* start signaling on USR1 */
    sa.sa_handler = set_signal_parents;
    sigfillset(&sa.sa_mask);
    if (debug_flag  >= 7)
        printf("%d: setting SIGUSR1 -> set_signal_parents\n", mypid);
    k_sigaction(SIGUSR1, &sa, 0);
    /* stop signaling on USR2 */
    sa.sa_handler = clear_signal_parents;
    if (debug_flag  >= 7)
        printf("%d: setting SIGUSR2 -> clear_signal_parents\n", mypid);
    k_sigaction(SIGUSR2, &sa, 0);

    fork_procs(num_procs);
    sleep(1); /* wait a sec to let all the children pause */

    /* now set up my signal handling */

    /* continue on ALRM */
    sa.sa_handler = wakeup;
    if (debug_flag >= 4)
        printf("%d: setting SIGALRM -> wakeup\n", mypid);
    k_sigaction(SIGALRM, &sa, 0);
    /* mark ready confirmation on USR1 */
    sa.sa_handler = set_confirmed_ready;
    if (debug_flag  >= 4)
        printf("%d: setting SIGUSR1 -> set_confirmed_ready\n", mypid);
    k_sigaction(SIGUSR1, &sa, 0);
    /* reset our counter on HUP */
    sa.sa_handler = reset_counter;
    if (debug_flag  >= 4)
        printf("%d: setting SIGHUP -> reset_counter\n", mypid);
    k_sigaction(SIGHUP, &sa, 0);

    /* reply to child on USR2 */
    sa.sa_handler = NULL;
    sa.sa_sigaction = reply_to_child;
    sa.sa_flags = SA_SIGINFO;
    if (debug_flag  >= 4)
        printf("%d: setting SIGUSR2 -> reply_to_child\n", mypid);
    k_sigaction(SIGUSR2, &sa, 0);

    /* tell our parent that we are ready to rock */
    while (!confirmed_ready_flag) {
        if (debug_flag  >= 3)
            printf("%d: Manager, SIGUSR1 -> %d\n", mypid, getppid());
        if (kill(getppid(), SIGUSR1) == -1) {
            tst_resm(TWARN, "%d: Couldn't signal master (%d) that we're ready. %d: %s",
                            mypid, getppid(), errno, strerror(errno));
            exit(errno);
        }
        usleep(100);
    }
    
    /* handle pgroup management while the tests are running */
    while (1) {
        alarm(1);
        if (debug_flag  >= 5) 
            printf("%d: Manager pausing (%d/%d)\n", 
                            mypid, child_signal_counter, num_procs); 
        pause(); 
        if (child_signal_counter >= num_procs) {
            confirmed_ready_flag = 0;
            tst_resm(TINFO, "%d: All %d children reported in", mypid, child_signal_counter);
            while (child_signal_counter) {
                if (debug_flag  >= 3)
                    printf("%d: Manager, SIGUSR2 -> %d\n", mypid, getppid());
                if (kill(getppid(), SIGUSR2) == -1) {
                    tst_resm(TINFO, "%d: Couldn't signal master (%d) that we're ready. %d: %s",
                                    mypid, getppid(), errno, strerror(errno));
                    exit(errno);
                }
                usleep(100);
            }
        }
    }
}

/* some simple signal handlers for the kids */
void 
graceful_exit(int sig) 
{ 
    exit(0); 
}
void 
set_signal_parents(int sig) 
{ 
    if (debug_flag  >= 8) 
        printf("%d: Child start signalling\n", mypid);
    signal_parents_flag = 1; 
}
void 
clear_signal_parents(int sig) 
{ 
    if (debug_flag  >= 8)
        printf("%d: Child stop signalling\n", mypid);
    signal_parents_flag = 0; 
}
void 
set_confirmed_ready(int sig) 
{ 

    if (debug_flag  >= 3)
        printf("%d: Manager confirmed ready\n", mypid);
    confirmed_ready_flag = 1; 
}
void 
reset_counter(int sig) 
{ 
    checklist_reset(0xFF); 
    child_signal_counter = 0; 
    if (debug_flag  >= 3)
        printf("%d: reset_counter\n", mypid);
}

void 
reply_to_child(int sig, siginfo_t *si, void *data)
{
    struct pid_list_item findit, *result;

    findit.pid = si->si_pid;

    result = bsearch(&findit, child_checklist, child_checklist_total, 
                    sizeof(*child_checklist), checklist_cmp);
    if (result) {
        if (!result->flag) {
            if (debug_flag  >= 6)
                printf("%d: reply_to_child, SIGUSR1 -> %d\n", mypid, si->si_pid);
            kill(si->si_pid, SIGUSR2);
            ++child_signal_counter;
            result->flag = 1;
        } else {
            if (debug_flag  >= 6)
                printf("%d: reply_to_child, already told %d\n", mypid, si->si_pid);
        }
    } else {
        tst_brkm(TBROK, cleanup, 
                        "received unexpected signal from %d", si->si_pid);
    }
}

void wakeup(int sig) { return; }

/*************************************************
 * fork_procs() - create all the children
 ************************************************/
void
fork_procs(int procs_left)
{
    pid_t child;

    if (!(child_checklist = calloc(procs_left, sizeof(*child_checklist)))) {
        tst_brkm(TBROK, cleanup,
                        "%d: couldn't calloc child_checklist, errno=%d : %s",
                        mypid, errno, strerror(errno));
    }
    child_checklist_total = 0;
    while (procs_left) {
        if (debug_flag  >= 4)
            printf("%d: forking new Child\n", mypid);
        switch (child = fork()) {
                case -1: tst_brkm(TBROK, cleanup,
                                         "fork() failed in fork_procs(%d), errno=%d : %s",
                                         procs_left, errno, strerror(errno));
                         break;
                case 0: mypid = getpid();
                         while (1) { 
                            /* wait to start */
                            if (debug_flag  >= 8) 
                                printf("%d: Child pausing\n", mypid);
                            signal_parents_flag = 0;
                            pause(); 
                            
                            /* if we started, call mama */
                            while (signal_parents_flag) {
                                if (debug_flag  >= 6)
                                    printf("%d: child, SIGUSR2 -> %d\n",
                                                    mypid, getppid());
                                if (kill(getppid(), SIGUSR2) == -1) {
                                    /* something went wrong */
                                    tst_resm(TINFO, "%d: kill(ppid:%d, SIGUSR2) failed. %d: %s", 
                                                    mypid, getppid(), errno, strerror(errno));
                                    exit(errno);
                                }
                                usleep(100);
                            }
                        }
                        break;
                default:
                        child_checklist[child_checklist_total++].pid = child;
        }
        --procs_left;
    }
    qsort(child_checklist, child_checklist_total, sizeof(*child_checklist), checklist_cmp);
}


int 
checklist_cmp(const void *a, const void *b)
{
    const struct pid_list_item *pa = (const struct pid_list_item *)a;
    const struct pid_list_item *pb = (const struct pid_list_item *)b;

    return (pa->pid > pb->pid) - (pa->pid < pb->pid);
}

void 
checklist_reset(int bit)
{
    int i;
    for (i = 0; i < child_checklist_total; i++) {
        child_checklist[i].flag = child_checklist[i].flag & (~bit);
    }

}

inline int
k_sigaction(int sig, struct sigaction *sa, struct sigaction *osa)
{
    int ret;
    if ((ret = sigaction(sig, sa, osa)) == -1) {
        tst_brkm(TBROK, cleanup,
                        "sigaction(%d, ...) failed, errno %d: %s",
                        sig, errno, strerror(errno));
    } 
    return ret;
}