1 /* $OpenBSD: signal-stress.c,v 1.2 2021/09/20 16:39:40 claudio Exp $ */ 2 /* 3 * Written by Artur Grabowski <art@openbsd.org> 2004 Public Domain. 4 */ 5 #include <sys/types.h> 6 #include <sys/mman.h> 7 #include <sys/wait.h> 8 9 #include <signal.h> 10 #include <unistd.h> 11 #include <stdlib.h> 12 #include <err.h> 13 14 int nprocs, nsigs; 15 pid_t *pids; 16 pid_t next, prev; 17 sig_atomic_t usr1, usr2; 18 19 void 20 sighand(int sig) 21 { 22 if (sig == SIGUSR1 && ++usr1 <= nsigs) { 23 if (kill(next, sig)) 24 _exit(1); 25 } 26 if (sig == SIGUSR2 && ++usr2 <= nsigs) { 27 if (kill(prev, sig)) 28 _exit(1); 29 } 30 } 31 32 void 33 do_child(void) 34 { 35 int i; 36 sigset_t mask, oldmask; 37 38 /* 39 * Step 1 - suspend and wait for SIGCONT so that all siblings have 40 * been started before the next step. 41 */ 42 raise(SIGSTOP); 43 44 /* Find our neighbours. */ 45 for (i = 0; i < nprocs; i++) { 46 if (pids[i] != getpid()) 47 continue; 48 if (i + 1 == nprocs) 49 next = pids[0]; 50 else 51 next = pids[i + 1]; 52 if (i == 0) 53 prev = pids[nprocs - 1]; 54 else 55 prev = pids[i - 1]; 56 } 57 58 signal(SIGUSR1, sighand); 59 signal(SIGUSR2, sighand); 60 61 sigemptyset(&mask); 62 sigaddset(&mask, SIGUSR1); 63 sigaddset(&mask, SIGUSR2); 64 65 sigprocmask(SIG_BLOCK, &mask, &oldmask); 66 67 /* Step 2 - wait again until everyone is ready. */ 68 raise(SIGSTOP); 69 70 while (usr1 < nsigs || usr2 < nsigs) 71 sigsuspend(&oldmask); 72 73 /* Step 3 - wait again until everyone is ready. */ 74 raise(SIGSTOP); 75 } 76 77 void 78 wait_stopped(pid_t pid) 79 { 80 int status; 81 82 if (waitpid(pid, &status, WUNTRACED) != pid) 83 err(1, "waitpid"); 84 if (!WIFSTOPPED(status)) 85 errx(1, "child %d not stopped", pid); 86 } 87 88 void 89 cleanup(void) 90 { 91 int i; 92 93 for (i = 0; i < nprocs; i++) 94 kill(pids[i], 9); 95 } 96 97 void 98 alrmhand(int sig) 99 { 100 cleanup(); 101 _exit(1); 102 } 103 104 int 105 main() 106 { 107 int i; 108 pid_t pid; 109 110 nprocs = 35; 111 112 nsigs = 1000; 113 114 if ((pids = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, 115 MAP_ANON|MAP_SHARED, -1, 0)) == MAP_FAILED) 116 err(1, "mmap"); 117 118 for (i = 0; i < nprocs; i++) { 119 switch((pid = fork())) { 120 case 0: 121 do_child(); 122 _exit(0); 123 case -1: 124 err(1, "fork"); 125 } 126 pids[i] = pid; 127 } 128 129 atexit(cleanup); 130 signal(SIGALRM, alrmhand); 131 alarm(120); /* Die after two minutes. */ 132 133 /* Step 1. Wait until all children have went to sleep */ 134 for (i = 0; i < nprocs; i++) 135 wait_stopped(pids[i]); 136 /* And wake them */ 137 for (i = 0; i < nprocs; i++) 138 kill(pids[i], SIGCONT); 139 140 /* Step 2. Repeat. */ 141 for (i = 0; i < nprocs; i++) 142 wait_stopped(pids[i]); 143 for (i = 0; i < nprocs; i++) 144 kill(pids[i], SIGCONT); 145 146 /* 147 * Now all children are ready for action. 148 * Send the first signals and wait until they all exit. 149 */ 150 kill(pids[arc4random_uniform(nprocs)], SIGUSR1); 151 kill(pids[arc4random_uniform(nprocs)], SIGUSR2); 152 153 /* 154 * The signal game is running, now insert noise in the process. 155 */ 156 for (i = 0; i < nprocs; i++) { 157 pid_t pid = pids[arc4random_uniform(nprocs)]; 158 kill(pid, SIGSTOP); 159 wait_stopped(pid); 160 kill(pid, SIGCONT); 161 } 162 163 164 /* Step 3. Repeat. */ 165 for (i = 0; i < nprocs; i++) 166 wait_stopped(pids[i]); 167 for (i = 0; i < nprocs; i++) 168 kill(pids[i], SIGCONT); 169 170 /* Wait for everyone to finish. */ 171 for (i = 0; i < nprocs; i++) { 172 int status; 173 174 if (waitpid(pids[i], &status, WUNTRACED) != pids[i]) 175 err(1, "waitpid"); 176 if (!WIFEXITED(status)) 177 errx(1, "child %d not stopped (%d)", pids[i], status); 178 if (WEXITSTATUS(status) != 0) 179 warnx("child %d status: %d", i, status); 180 } 181 182 return (0); 183 } 184