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
sighand(int sig)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
do_child(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
wait_stopped(pid_t pid)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
cleanup(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
alrmhand(int sig)98 alrmhand(int sig)
99 {
100 cleanup();
101 _exit(1);
102 }
103
104 int
main()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