768 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			768 lines
		
	
	
		
			20 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.,
 | |
|  * 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: kill10.c,v 1.7 2009/03/23 13:35:53 subrata_modak 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"
 | |
| 
 | |
| 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);
 | |
| 
 | |
| static inline int k_sigaction(int sig, struct sigaction *sa, struct sigaction *osa);
 | |
| 
 | |
| char *TCID = "kill10";
 | |
| int TST_TOTAL = 1;
 | |
| 
 | |
| 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;
 | |
| 
 | |
| option_t options[] = {
 | |
| 	{"n:", &nflag, &narg},	/* -n #procs */
 | |
| 	{"g:", &gflag, &garg},	/* -g #pgrps */
 | |
| 	{"d:", &dflag, &darg},	/* -d <debug level>  */
 | |
| 	{NULL, NULL, NULL}
 | |
| };
 | |
| 
 | |
| int main(int ac, char **av)
 | |
| {
 | |
| 	int lc;
 | |
| 	int cnt;
 | |
| 
 | |
| 	tst_parse_opts(ac, av, options, &help);
 | |
| 
 | |
| 	if (nflag) {
 | |
| 		if (sscanf(narg, "%i", &num_procs) != 1) {
 | |
| 			tst_brkm(TBROK, NULL, "-n option arg is not a number");
 | |
| 		}
 | |
| 	}
 | |
| 	if (gflag) {
 | |
| 		if (sscanf(garg, "%i", &num_pgrps) != 1) {
 | |
| 			tst_brkm(TBROK, NULL, "-g option arg is not a number");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (dflag) {
 | |
| 		if (sscanf(darg, "%i", &debug_flag) != 1) {
 | |
| 			tst_brkm(TBROK, NULL, "-d option arg is not a number");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	setup();
 | |
| 
 | |
| 	for (lc = 0; TEST_LOOPING(lc); lc++) {
 | |
| 
 | |
| 		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);
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	cleanup();
 | |
| 
 | |
| 	tst_exit();
 | |
| }
 | |
| 
 | |
| void help(void)
 | |
| {
 | |
| 	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);
 | |
| }
 | |
| 
 | |
| void setup(void)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	/* 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, NULL);
 | |
| 	if (debug_flag >= 1)
 | |
| 		printf("%d: setting SIGCONT -> SIG_DFL\n", mypid);
 | |
| 	k_sigaction(SIGCONT, &sa, NULL);
 | |
| 
 | |
| 	sa.sa_handler = set_create_procs;
 | |
| 	if (debug_flag >= 4)
 | |
| 		printf("%d: setting SIGALRM -> set_create_procs\n", mypid);
 | |
| 	k_sigaction(SIGALRM, &sa, NULL);
 | |
| 
 | |
| 	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, NULL);
 | |
| 
 | |
| 	fork_pgrps(num_pgrps);
 | |
| 
 | |
| 	/* wait for all pgrps to report in */
 | |
| 	if (debug_flag)
 | |
| 		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);
 | |
| 		/*
 | |
| 		 * We might receive the signal from the (last manager) before
 | |
| 		 * we issue a pause. In that case we might hang even if we have
 | |
| 		 * all the managers reported in. So set an alarm so that we can
 | |
| 		 * wake up.
 | |
| 		 */
 | |
| 		alarm(1);
 | |
| 
 | |
| 		pause();
 | |
| 	}
 | |
| 	checklist_reset(0x03);
 | |
| 	if (debug_flag) {
 | |
| 		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, NULL);
 | |
| 	/* 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, NULL);
 | |
| }
 | |
| 
 | |
| 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 {
 | |
| 		printf("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 {
 | |
| 		printf("received unexpected signal %d from %d",
 | |
| 		       sig, si->si_pid);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /***************************************************************
 | |
|  * cleanup() - performs all ONE TIME cleanup for this test at
 | |
|  *		completion or premature exit.
 | |
|  ***************************************************************/
 | |
| void cleanup(void)
 | |
| {
 | |
| 	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);
 | |
| 	}
 | |
| 	free(child_checklist);
 | |
| 
 | |
| }
 | |
| 
 | |
| /*********************************************************************
 | |
|  * 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 | TERRNO, cleanup,
 | |
| 				 "fork() failed in fork_pgrps(%d)", pgrps_left);
 | |
| 			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 HUP */
 | |
| 	sa.sa_handler = SIG_IGN;
 | |
| 	sigemptyset(&sa.sa_mask);
 | |
| 	sa.sa_flags = 0;
 | |
| 	if (debug_flag >= 4)
 | |
| 		printf("%d: setting SIGHUP -> SIG_IGN\n", mypid);
 | |
| 	k_sigaction(SIGHUP, &sa, NULL);
 | |
| 
 | |
| 	/* We use ALRM to make sure that we don't miss the signal effects ! */
 | |
| 	sa.sa_handler = wakeup;
 | |
| 	if (debug_flag >= 4)
 | |
| 		printf("%d: setting SIGALRM -> wakeup\n", mypid);
 | |
| 	k_sigaction(SIGALRM, &sa, NULL);
 | |
| 
 | |
| 	/* exit on QUIT */
 | |
| 	sa.sa_handler = graceful_exit;
 | |
| 	if (debug_flag >= 4)
 | |
| 		printf("%d: setting SIGQUIT -> graceful_exit\n", mypid);
 | |
| 	k_sigaction(SIGQUIT, &sa, NULL);
 | |
| 
 | |
| 	/* 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, NULL);
 | |
| 	/* 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, NULL);
 | |
| 
 | |
| 	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, NULL);
 | |
| 	/* 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, NULL);
 | |
| 	/* 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, NULL);
 | |
| 
 | |
| 	/* 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, NULL);
 | |
| 
 | |
| 	/* 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) {
 | |
| 			printf("%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;
 | |
| 			printf("%d: All %d children reported in\n",
 | |
| 			       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) {
 | |
| 					printf("%d: Couldn't signal master "
 | |
| 					       "(%d) that we're ready. %d: %s\n",
 | |
| 					       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 signaling\n", mypid);
 | |
| 	signal_parents_flag = 1;
 | |
| }
 | |
| 
 | |
| void clear_signal_parents(int sig)
 | |
| {
 | |
| 	if (debug_flag >= 8)
 | |
| 		printf("%d: Child stop signaling\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;
 | |
| 
 | |
| 	/* We are setting the flag for children, to avoid missing any signals */
 | |
| 	signal_parents_flag = 0;
 | |
| 
 | |
| 	while (procs_left) {
 | |
| 		if (debug_flag >= 4)
 | |
| 			printf("%d: forking new child\n", mypid);
 | |
| 		switch (child = fork()) {
 | |
| 		case -1:
 | |
| 			tst_brkm(TBROK | TERRNO, cleanup,
 | |
| 				 "fork() failed in fork_procs(%d)", procs_left);
 | |
| 			break;
 | |
| 		case 0:
 | |
| 			mypid = getpid();
 | |
| 			while (1) {
 | |
| 				/* wait to start */
 | |
| 				if (debug_flag >= 8)
 | |
| 					printf("%d: child pausing\n", mypid);
 | |
| 				/*
 | |
| 				 * If we have already received the signal, we dont
 | |
| 				 * want to pause for it !
 | |
| 				 */
 | |
| 				while (!signal_parents_flag) {
 | |
| 					alarm(2);
 | |
| 					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 */
 | |
| 						printf("%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);
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| static inline int k_sigaction(int sig, struct sigaction *sa, struct sigaction *osa)
 | |
| {
 | |
| 	int ret;
 | |
| 	if ((ret = sigaction(sig, sa, osa)) == -1) {
 | |
| 		tst_brkm(TBROK | TERRNO, cleanup, "sigaction(%d, ...) failed",
 | |
| 			 sig);
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 |