/* * 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 #include #include #include #include #include #include #include #include #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 */ {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; }