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 14int nprocs, nsigs; 15pid_t *pids; 16pid_t next, prev; 17sig_atomic_t usr1, usr2; 18 19void 20sighand(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 32void 33do_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 77void 78wait_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 88void 89cleanup(void) 90{ 91 int i; 92 93 for (i = 0; i < nprocs; i++) 94 kill(pids[i], 9); 95} 96 97void 98alrmhand(int sig) 99{ 100 cleanup(); 101 _exit(1); 102} 103 104int 105main() 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