xref: /freebsd/tools/test/stress2/misc/kevent19.sh (revision df21a004be237a1dccd03c7b47254625eea62fa9)
1#!/bin/sh
2
3# A kqueuex(KQUEUE_CPONFORK) test scenario
4
5set -u
6prog=$(basename "$0" .sh)
7
8cat > /tmp/$prog.c <<EOF
9/* \$Id: kqfork.c,v 1.4 2025/08/19 19:42:16 kostik Exp kostik $ */
10
11#include <sys/param.h>
12#include <sys/event.h>
13#include <err.h>
14#include <signal.h>
15#include <stdbool.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h>
20
21#ifndef KQUEUE_CPONFORK
22#define	KQUEUE_CPONFORK	0x2
23#endif
24
25static pid_t pid_pipe_beat;
26static pid_t pid_controller;
27
28static void
29sighup_handler(int sig __unused)
30{
31	kill(pid_pipe_beat, SIGKILL);
32	_exit(1);
33}
34
35static void
36pipe_beat(int wp)
37{
38	static const char a[1] = { 'a' };
39	ssize_t s;
40
41	for (;;) {
42		sleep(1);
43		s = write(wp, a, 1);
44		if (s < 0)
45			err(1, "pipe write");
46		if (s == 0)
47			errx(1, "short pipe write");
48	}
49}
50
51static void
52worker(int kq, int rp)
53{
54	struct kevent ev[1];
55	char a[1];
56	ssize_t s;
57	int n;
58
59	for (;;) {
60		n = kevent(kq, NULL, 0, ev, nitems(ev), NULL);
61		if (n == -1) {
62			kill(pid_controller, SIGHUP);
63			err(1, "kqueue");
64		}
65		if (n == 0)
66			continue; // XXXKIB
67		switch (ev[0].filter) {
68		case EVFILT_TIMER:
69			printf("tick\n");
70			break;
71		case EVFILT_READ:
72			if (ev[0].ident != (uintptr_t)rp) {
73				kill(pid_controller, SIGHUP);
74				errx(1, "unknown read ident %d\n", (int)ev[0].ident);
75			}
76			s = read(rp, a, sizeof(a));
77			if (s == -1) {
78				kill(pid_controller, SIGHUP);
79				err(1, "read");
80			}
81			if (s == 0) {
82				kill(pid_controller, SIGHUP);
83				errx(1, "EOF");
84			}
85			printf("%c\n", a[0]);
86			break;
87		default:
88			kill(pid_controller, SIGHUP);
89			errx(1, "unknown fiter %d\n", ev[0].filter);
90			break;
91		}
92	}
93}
94
95static void
96usage(void)
97{
98	fprintf(stderr, "Usage: kqfork [fork]\n");
99	exit(2);
100}
101
102int
103main(int argc, char *argv[])
104{
105	struct kevent ev[2];
106	struct sigaction sa;
107	int kq, n, pp[2];
108	pid_t pid_worker;
109	bool do_fork;
110
111	do_fork = false;
112	if (argc != 1 && argc != 2)
113		usage();
114	if (argc == 2) {
115		if (strcmp(argv[1], "fork") != 0)
116			usage();
117		do_fork = true;
118	}
119
120	memset(&sa, 0, sizeof(sa));
121	sa.sa_handler = sighup_handler;
122	if (sigaction(SIGHUP, &sa, NULL) == -1)
123		err(1, "sigaction(SIGHUP)");
124
125	memset(&sa, 0, sizeof(sa));
126	sa.sa_flags = SA_NOCLDWAIT | SA_NOCLDSTOP;
127	sa.sa_handler = SIG_IGN;
128	if (sigaction(SIGCHLD, &sa, NULL) == -1)
129		err(1, "sigaction(SIGCHLD)");
130
131	if (pipe(pp) == -1)
132		err(1, "pipe");
133
134	pid_pipe_beat = fork();
135	if (pid_pipe_beat == -1)
136		err(1, "fork");
137	if (pid_pipe_beat == 0) {
138		close(pp[0]);
139		pipe_beat(pp[1]);
140	}
141
142	kq = kqueuex(do_fork ? KQUEUE_CPONFORK : 0);
143	if (kq == -1)
144		err(1, "kqueuex");
145
146	EV_SET(&ev[0], 1, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 1, NULL);
147	EV_SET(&ev[1], pp[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
148	n = kevent(kq, ev, nitems(ev), NULL, 0, NULL);
149	if (n == -1) {
150		kill(pid_pipe_beat, SIGKILL);
151		err(1, "kevent reg");
152	}
153	if (n != 0) {
154		kill(pid_pipe_beat, SIGKILL);
155		errx(1, "kevent reg %d", n);
156	}
157
158	pid_controller = getpid();
159
160	if (do_fork) {
161		pid_worker = fork();
162		if (pid_worker == -1) {
163			kill(pid_pipe_beat, SIGKILL);
164			err(1, "fork");
165		}
166		if (pid_worker == 0) {
167			close(pp[1]);
168			worker(kq, pp[0]);
169		}
170
171		for (;;)
172			pause();
173	} else {
174		worker(kq, pp[0]);
175	}
176	exit(0); // unreachable
177}
178EOF
179cc -o /tmp/$prog -Wall -Wextra -O2 /tmp/$prog.c || exit 1
180
181echo "--> No fork"
182timeout 4s /tmp/$prog
183echo "--> fork"
184timeout 4s /tmp/$prog fork
185
186rm -f /tmp/$prog.c $prog
187exit 0
188