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