etc/services - sync with NetBSD-8
[minix.git] / minix / tests / test79.c
blobb5948c67355356442f365d0d613aeb9224c77262
1 /* Tests for PM signal handling robustness - by D.C. van Moolenbroek */
2 /*
3 * The signal handling code must not rely on priorities assigned to services,
4 * and so, this test (like any test!) must also pass if PM and/or VFS are not
5 * given a fixed high priority. A good way to verify this is to let PM and VFS
6 * be scheduled by SCHED rather than KERNEL, and to give them the same priority
7 * as (or slightly lower than) normal user processes. Note that if VFS is
8 * configured to use a priority *far lower* than user processes, starvation may
9 * cause this test not to complete in some scenarios. In that case, Ctrl+C
10 * should still be able to kill the test.
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <signal.h>
15 #include <sys/wait.h>
16 #include <sys/time.h>
17 #include <sys/utsname.h>
19 #define ITERATIONS 1
21 #include "common.h"
23 #define NR_SIGNALS 20000
25 #define MAX_SIGNALERS 3
27 static const int signaler_sig[MAX_SIGNALERS] = { SIGUSR1, SIGUSR2, SIGHUP };
28 static pid_t signaler_pid[MAX_SIGNALERS];
29 static int sig_counter;
31 enum {
32 JOB_RUN = 0,
33 JOB_CALL_PM,
34 JOB_CALL_VFS,
35 JOB_SET_MASK,
36 JOB_BLOCK_PM,
37 JOB_BLOCK_VFS,
38 JOB_CALL_PM_VFS,
39 JOB_FORK,
40 NR_JOBS
43 #define OPT_NEST 0x1
44 #define OPT_ALARM 0x2
45 #define OPT_ALL 0x3
47 struct link {
48 pid_t pid;
49 int sndfd;
50 int rcvfd;
54 * Spawn a child process, with a pair of pipes to talk to it bidirectionally.
56 static void
57 spawn(struct link *link, void (*proc)(struct link *))
59 int up[2], dn[2];
61 fflush(stdout);
62 fflush(stderr);
64 if (pipe(up) != 0) e(0);
65 if (pipe(dn) != 0) e(0);
67 link->pid = fork();
69 switch (link->pid) {
70 case 0:
71 close(up[1]);
72 close(dn[0]);
74 link->rcvfd = up[0];
75 link->sndfd = dn[1];
77 errct = 0;
79 proc(link);
81 /* Close our pipe FDs on exit, so that we can make zombies. */
82 exit(errct);
83 case -1:
84 e(0);
85 break;
88 close(up[0]);
89 close(dn[1]);
91 link->sndfd = up[1];
92 link->rcvfd = dn[0];
96 * Wait for a child process to terminate, and clean up.
98 static void
99 collect(struct link *link)
101 int status;
103 close(link->sndfd);
104 close(link->rcvfd);
106 if (waitpid(link->pid, &status, 0) <= 0) e(0);
108 if (!WIFEXITED(status)) e(0);
109 else errct += WEXITSTATUS(status);
113 * Forcibly terminate a child process, and clean up.
115 static void
116 terminate(struct link *link)
118 int status;
120 if (kill(link->pid, SIGKILL) != 0) e(0);
122 close(link->sndfd);
123 close(link->rcvfd);
125 if (waitpid(link->pid, &status, 0) <= 0) e(0);
127 if (WIFSIGNALED(status)) {
128 if (WTERMSIG(status) != SIGKILL) e(0);
129 } else {
130 if (!WIFEXITED(status)) e(0);
131 else errct += WEXITSTATUS(status);
136 * Send an integer value to the child or parent.
138 static void
139 snd(struct link *link, int val)
141 if (write(link->sndfd, (void *) &val, sizeof(val)) != sizeof(val))
142 e(0);
146 * Receive an integer value from the child or parent, or -1 on EOF.
148 static int
149 rcv(struct link *link)
151 int r, val;
153 if ((r = read(link->rcvfd, (void *) &val, sizeof(val))) == 0)
154 return -1;
156 if (r != sizeof(val)) e(0);
158 return val;
162 * Set a signal handler for a particular signal, blocking either all or no
163 * signals when the signal handler is invoked.
165 static void
166 set_handler(int sig, void (*proc)(int), int block)
168 struct sigaction act;
170 memset(&act, 0, sizeof(act));
171 if (block) sigfillset(&act.sa_mask);
172 act.sa_handler = proc;
174 if (sigaction(sig, &act, NULL) != 0) e(0);
178 * Generic signal handler for the worker process.
180 static void
181 worker_handler(int sig)
183 int i;
185 switch (sig) {
186 case SIGUSR1:
187 case SIGUSR2:
188 case SIGHUP:
189 for (i = 0; i < MAX_SIGNALERS; i++) {
190 if (signaler_sig[i] != sig) continue;
192 if (signaler_pid[i] == -1) e(0);
193 else if (kill(signaler_pid[i], SIGUSR1) != 0) e(0);
194 break;
196 if (i == MAX_SIGNALERS) e(0);
197 break;
198 case SIGTERM:
199 exit(errct);
200 break;
201 case SIGALRM:
202 /* Do nothing. */
203 break;
204 default:
205 e(0);
210 * Procedure for the worker process. Sets up its own environment using
211 * information sent to it by the parent, sends an acknowledgement to the
212 * parent, and loops executing the job given to it until a SIGTERM comes in.
214 static void __dead
215 worker_proc(struct link *parent)
217 struct utsname name;
218 struct itimerval it;
219 struct timeval tv;
220 sigset_t set, oset;
221 uid_t uid;
222 int i, job, options;
224 job = rcv(parent);
225 options = rcv(parent);
227 for (i = 0; i < MAX_SIGNALERS; i++) {
228 set_handler(signaler_sig[i], worker_handler,
229 !(options & OPT_NEST));
231 signaler_pid[i] = rcv(parent);
234 set_handler(SIGTERM, worker_handler, 1 /* block */);
235 set_handler(SIGALRM, worker_handler, !(options & OPT_NEST));
237 snd(parent, 0);
239 if (options & OPT_ALARM) {
240 /* The timer would kill wimpy platforms such as ARM. */
241 if (uname(&name) < 0) e(0);
242 if (strcmp(name.machine, "arm")) {
243 it.it_value.tv_sec = 0;
244 it.it_value.tv_usec = 1;
245 it.it_interval.tv_sec = 0;
246 it.it_interval.tv_usec = 1;
247 if (setitimer(ITIMER_REAL, &it, NULL) != 0) e(0);
251 switch (job) {
252 case JOB_RUN:
253 for (;;);
254 break;
255 case JOB_CALL_PM:
257 * Part of the complication of the current system in PM comes
258 * from the fact that when a process is being stopped, it might
259 * already have started sending a message. That message will
260 * arrive at its destination regardless of the process's run
261 * state. PM must avoid setting up a signal handler (and
262 * changing the process's signal mask as part of that) if such
263 * a message is still in transit, because that message might,
264 * for example, query (or even change) the signal mask.
266 for (;;) {
267 if (sigprocmask(SIG_BLOCK, NULL, &set) != 0) e(0);
268 if (sigismember(&set, SIGUSR1)) e(0);
270 break;
271 case JOB_CALL_VFS:
272 for (;;) {
273 tv.tv_sec = 0;
274 tv.tv_usec = 0;
275 select(0, NULL, NULL, NULL, &tv);
277 break;
278 case JOB_SET_MASK:
279 for (;;) {
280 sigfillset(&set);
281 if (sigprocmask(SIG_SETMASK, &set, &oset) != 0) e(0);
282 if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) e(0);
284 break;
285 case JOB_BLOCK_PM:
286 for (;;) {
287 sigemptyset(&set);
288 sigsuspend(&set);
290 break;
291 case JOB_BLOCK_VFS:
292 for (;;)
293 select(0, NULL, NULL, NULL, NULL);
294 break;
295 case JOB_CALL_PM_VFS:
296 uid = getuid();
297 for (;;)
298 setuid(uid);
299 break;
300 case JOB_FORK:
302 * The child exits immediately; the parent kills the child
303 * immediately. The outcome mostly depends on scheduling.
304 * Varying process priorities may yield different tests.
306 for (;;) {
307 pid_t pid = fork();
308 switch (pid) {
309 case 0:
310 exit(0);
311 case -1:
312 e(1);
313 break;
314 default:
315 kill(pid, SIGKILL);
316 if (wait(NULL) != pid) e(0);
319 break;
320 default:
321 e(0);
322 exit(1);
327 * Signal handler procedure for the signaler processes, counting the number of
328 * signals received from the worker process.
330 static void
331 signaler_handler(int sig)
333 sig_counter++;
337 * Procedure for the signaler processes. Gets the pid of the worker process
338 * and the signal to use, and then repeatedly sends that signal to the worker
339 * process, waiting for a SIGUSR1 signal back from the worker before
340 * continuing. This signal ping-pong is repeated for a set number of times.
342 static void
343 signaler_proc(struct link *parent)
345 sigset_t set, oset;
346 pid_t pid;
347 int i, sig, nr;
349 pid = rcv(parent);
350 sig = rcv(parent);
351 nr = rcv(parent);
352 sig_counter = 0;
354 sigfillset(&set);
355 if (sigprocmask(SIG_SETMASK, &set, &oset) != 0) e(0);
357 set_handler(SIGUSR1, signaler_handler, 1 /*block*/);
359 for (i = 0; nr == 0 || i < nr; i++) {
360 if (sig_counter != i) e(0);
362 if (kill(pid, sig) != 0 && nr > 0) e(0);
364 sigsuspend(&oset);
367 if (sig_counter != nr) e(0);
371 * Set up the worker and signaler processes, wait for the signaler processes to
372 * do their work and terminate, and then terminate the worker process.
374 static void
375 sub79a(int job, int signalers, int options)
377 struct link worker, signaler[MAX_SIGNALERS];
378 int i;
380 spawn(&worker, worker_proc);
382 snd(&worker, job);
383 snd(&worker, options);
385 for (i = 0; i < signalers; i++) {
386 spawn(&signaler[i], signaler_proc);
388 snd(&worker, signaler[i].pid);
390 for (; i < MAX_SIGNALERS; i++)
391 snd(&worker, -1);
393 if (rcv(&worker) != 0) e(0);
395 for (i = 0; i < signalers; i++) {
396 snd(&signaler[i], worker.pid);
397 snd(&signaler[i], signaler_sig[i]);
398 snd(&signaler[i], NR_SIGNALS);
401 for (i = 0; i < signalers; i++)
402 collect(&signaler[i]);
404 if (kill(worker.pid, SIGTERM) != 0) e(0);
406 collect(&worker);
410 * Stress test for signal handling. One worker process gets signals from up to
411 * three signaler processes while performing one of a number of jobs. It
412 * replies to each signal by signaling the source, thus creating a ping-pong
413 * effect for each of the signaler processes. The signal ping-ponging is
414 * supposed to be reliable, and the most important aspect of the test is that
415 * no signals get lost. The test is performed a number of times, varying the
416 * job executed by the worker process, the number of signalers, whether signals
417 * are blocked while executing a signal handler in the worker, and whether the
418 * worker process has a timer running at high frequency.
420 static void
421 test79a(void)
423 int job, signalers, options;
425 subtest = 1;
427 for (options = 0; options <= OPT_ALL; options++)
428 for (signalers = 1; signalers <= MAX_SIGNALERS; signalers++)
429 for (job = 0; job < NR_JOBS; job++)
430 sub79a(job, signalers, options);
434 * Set up the worker process and optionally a signaler process, wait for a
435 * predetermined amount of time, and then kill all the child processes.
437 static void
438 sub79b(int job, int use_signaler, int options)
440 struct link worker, signaler;
441 struct timeval tv;
442 int i;
444 spawn(&worker, worker_proc);
446 snd(&worker, job);
447 snd(&worker, options);
449 if ((i = use_signaler) != 0) {
450 spawn(&signaler, signaler_proc);
452 snd(&worker, signaler.pid);
454 for (; i < MAX_SIGNALERS; i++)
455 snd(&worker, -1);
457 if (rcv(&worker) != 0) e(0);
459 if (use_signaler) {
460 snd(&signaler, worker.pid);
461 snd(&signaler, signaler_sig[0]);
462 snd(&signaler, 0);
465 /* Use select() so that we can verify we don't get signals. */
466 tv.tv_sec = 0;
467 tv.tv_usec = 100000;
468 if (select(0, NULL, NULL, NULL, &tv) != 0) e(0);
470 terminate(&worker);
472 if (use_signaler)
473 terminate(&signaler);
477 * This test is similar to the previous one, except that we now kill the worker
478 * process after a while. This should trigger various process transitions to
479 * the exiting state. Not much can be verified from this test program, but we
480 * intend to trigger as many internal state verification statements of PM
481 * itself as possible this way. A signaler process is optional in this test,
482 * and if used, it will not stop after a predetermined number of signals.
484 static void
485 test79b(void)
487 int job, signalers, options;
489 subtest = 2;
491 for (options = 0; options <= OPT_ALL; options++)
492 for (signalers = 0; signalers <= 1; signalers++)
493 for (job = 0; job < NR_JOBS; job++)
494 sub79b(job, signalers, options);
499 * PM signal handling robustness test program.
502 main(int argc, char **argv)
504 int i, m;
506 start(79);
508 if (argc == 2)
509 m = atoi(argv[1]);
510 else
511 m = 0xFF;
513 for (i = 0; i < ITERATIONS; i++) {
514 if (m & 0x01) test79a();
515 if (m & 0x02) test79b();
518 quit();