xref: /xnu-11215.61.5/tests/kevent_pty.c (revision 4f1223e81cd707a65cc109d0b8ad6653699da3c4)
1*4f1223e8SApple OSS Distributions #ifdef T_NAMESPACE
2*4f1223e8SApple OSS Distributions #undef T_NAMESPACE
3*4f1223e8SApple OSS Distributions #endif /* T_NAMESPACE */
4*4f1223e8SApple OSS Distributions 
5*4f1223e8SApple OSS Distributions #include <Block.h>
6*4f1223e8SApple OSS Distributions #include <darwintest.h>
7*4f1223e8SApple OSS Distributions #include <dispatch/dispatch.h>
8*4f1223e8SApple OSS Distributions #include <err.h>
9*4f1223e8SApple OSS Distributions #include <fcntl.h>
10*4f1223e8SApple OSS Distributions #include <limits.h>
11*4f1223e8SApple OSS Distributions #include <signal.h>
12*4f1223e8SApple OSS Distributions #include <stdbool.h>
13*4f1223e8SApple OSS Distributions #include <stdlib.h>
14*4f1223e8SApple OSS Distributions #include <stdint.h>
15*4f1223e8SApple OSS Distributions #include <unistd.h>
16*4f1223e8SApple OSS Distributions #include <util.h>
17*4f1223e8SApple OSS Distributions 
18*4f1223e8SApple OSS Distributions T_GLOBAL_META(
19*4f1223e8SApple OSS Distributions 	T_META_NAMESPACE("xnu.kevent"),
20*4f1223e8SApple OSS Distributions 	T_META_RADAR_COMPONENT_NAME("xnu"),
21*4f1223e8SApple OSS Distributions 	T_META_RADAR_COMPONENT_VERSION("kevent"),
22*4f1223e8SApple OSS Distributions 	T_META_CHECK_LEAKS(false),
23*4f1223e8SApple OSS Distributions 	T_META_RUN_CONCURRENTLY(true));
24*4f1223e8SApple OSS Distributions 
25*4f1223e8SApple OSS Distributions #define TIMEOUT_SECS 10
26*4f1223e8SApple OSS Distributions 
27*4f1223e8SApple OSS Distributions static int child_ready[2];
28*4f1223e8SApple OSS Distributions 
29*4f1223e8SApple OSS Distributions static void
child_tty_client(void)30*4f1223e8SApple OSS Distributions child_tty_client(void)
31*4f1223e8SApple OSS Distributions {
32*4f1223e8SApple OSS Distributions 	dispatch_source_t src;
33*4f1223e8SApple OSS Distributions 	char buf[16] = "";
34*4f1223e8SApple OSS Distributions 	ssize_t bytes_wr;
35*4f1223e8SApple OSS Distributions 
36*4f1223e8SApple OSS Distributions 	src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
37*4f1223e8SApple OSS Distributions 	    (uintptr_t)STDIN_FILENO, 0, NULL);
38*4f1223e8SApple OSS Distributions 	if (!src) {
39*4f1223e8SApple OSS Distributions 		exit(1);
40*4f1223e8SApple OSS Distributions 	}
41*4f1223e8SApple OSS Distributions 	dispatch_source_set_event_handler(src, ^{});
42*4f1223e8SApple OSS Distributions 
43*4f1223e8SApple OSS Distributions 	dispatch_activate(src);
44*4f1223e8SApple OSS Distributions 
45*4f1223e8SApple OSS Distributions 	close(child_ready[0]);
46*4f1223e8SApple OSS Distributions 	snprintf(buf, sizeof(buf), "%ds", getpid());
47*4f1223e8SApple OSS Distributions 	bytes_wr = write(child_ready[1], buf, strlen(buf));
48*4f1223e8SApple OSS Distributions 	if (bytes_wr < 0) {
49*4f1223e8SApple OSS Distributions 		err(1, "failed to write on child ready pipe");
50*4f1223e8SApple OSS Distributions 	}
51*4f1223e8SApple OSS Distributions 
52*4f1223e8SApple OSS Distributions 	dispatch_main();
53*4f1223e8SApple OSS Distributions }
54*4f1223e8SApple OSS Distributions 
55*4f1223e8SApple OSS Distributions static void
pty_master(void)56*4f1223e8SApple OSS Distributions pty_master(void)
57*4f1223e8SApple OSS Distributions {
58*4f1223e8SApple OSS Distributions 	pid_t child_pid;
59*4f1223e8SApple OSS Distributions 	int ret;
60*4f1223e8SApple OSS Distributions 
61*4f1223e8SApple OSS Distributions 	child_pid = fork();
62*4f1223e8SApple OSS Distributions 	if (child_pid == 0) {
63*4f1223e8SApple OSS Distributions 		child_tty_client();
64*4f1223e8SApple OSS Distributions 	}
65*4f1223e8SApple OSS Distributions 	ret = setpgid(child_pid, child_pid);
66*4f1223e8SApple OSS Distributions 	if (ret < 0) {
67*4f1223e8SApple OSS Distributions 		exit(1);
68*4f1223e8SApple OSS Distributions 	}
69*4f1223e8SApple OSS Distributions 	ret = tcsetpgrp(STDIN_FILENO, child_pid);
70*4f1223e8SApple OSS Distributions 	if (ret < 0) {
71*4f1223e8SApple OSS Distributions 		exit(1);
72*4f1223e8SApple OSS Distributions 	}
73*4f1223e8SApple OSS Distributions 
74*4f1223e8SApple OSS Distributions 	sleep(TIMEOUT_SECS);
75*4f1223e8SApple OSS Distributions 	exit(1);
76*4f1223e8SApple OSS Distributions }
77*4f1223e8SApple OSS Distributions 
78*4f1223e8SApple OSS Distributions T_DECL(pty_master_teardown,
79*4f1223e8SApple OSS Distributions     "try removing a TTY master out from under a PTY slave holding a kevent",
80*4f1223e8SApple OSS Distributions     T_META_ASROOT(true), T_META_TAG_VM_PREFERRED)
81*4f1223e8SApple OSS Distributions {
82*4f1223e8SApple OSS Distributions 	__block pid_t master_pid;
83*4f1223e8SApple OSS Distributions 	char buf[16] = "";
84*4f1223e8SApple OSS Distributions 	char *end;
85*4f1223e8SApple OSS Distributions 	ssize_t bytes_rd;
86*4f1223e8SApple OSS Distributions 	size_t buf_len = 0;
87*4f1223e8SApple OSS Distributions 	unsigned long slave_pid;
88*4f1223e8SApple OSS Distributions 	int master_fd;
89*4f1223e8SApple OSS Distributions 	char pty_filename[PATH_MAX];
90*4f1223e8SApple OSS Distributions 	int status;
91*4f1223e8SApple OSS Distributions 
92*4f1223e8SApple OSS Distributions 	T_SETUPBEGIN;
93*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(pipe(child_ready), NULL);
94*4f1223e8SApple OSS Distributions 
95*4f1223e8SApple OSS Distributions 	master_pid = forkpty(&master_fd, pty_filename, NULL, NULL);
96*4f1223e8SApple OSS Distributions 	if (master_pid == 0) {
97*4f1223e8SApple OSS Distributions 		pty_master();
98*4f1223e8SApple OSS Distributions 		__builtin_unreachable();
99*4f1223e8SApple OSS Distributions 	}
100*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(master_pid,
101*4f1223e8SApple OSS Distributions 	    "forked child master PTY with pid %d, at pty %s", master_pid,
102*4f1223e8SApple OSS Distributions 	    pty_filename);
103*4f1223e8SApple OSS Distributions 
104*4f1223e8SApple OSS Distributions 	close(child_ready[1]);
105*4f1223e8SApple OSS Distributions 
106*4f1223e8SApple OSS Distributions 	end = buf;
107*4f1223e8SApple OSS Distributions 	do {
108*4f1223e8SApple OSS Distributions 		bytes_rd = read(child_ready[0], end, sizeof(buf) - buf_len);
109*4f1223e8SApple OSS Distributions 		T_ASSERT_POSIX_SUCCESS(bytes_rd, "read on pipe between master and runner");
110*4f1223e8SApple OSS Distributions 		buf_len += (size_t)bytes_rd;
111*4f1223e8SApple OSS Distributions 		T_LOG("runner read %zd bytes", bytes_rd);
112*4f1223e8SApple OSS Distributions 		end += bytes_rd;
113*4f1223e8SApple OSS Distributions 	} while (bytes_rd != 0 && *(end - 1) != 's');
114*4f1223e8SApple OSS Distributions 
115*4f1223e8SApple OSS Distributions 	slave_pid = strtoul(buf, &end, 0);
116*4f1223e8SApple OSS Distributions 	if (buf == end) {
117*4f1223e8SApple OSS Distributions 		T_ASSERT_FAIL("could not parse child PID from master pipe");
118*4f1223e8SApple OSS Distributions 	}
119*4f1223e8SApple OSS Distributions 
120*4f1223e8SApple OSS Distributions 	T_LOG("got pid %lu for slave process from master", slave_pid);
121*4f1223e8SApple OSS Distributions 	T_SETUPEND;
122*4f1223e8SApple OSS Distributions 
123*4f1223e8SApple OSS Distributions 	T_LOG("sending fatal signal to master");
124*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(kill(master_pid, SIGKILL), NULL);
125*4f1223e8SApple OSS Distributions 
126*4f1223e8SApple OSS Distributions 	T_LOG("sending fatal signal to slave");
127*4f1223e8SApple OSS Distributions 	(void)kill((int)slave_pid, SIGKILL);
128*4f1223e8SApple OSS Distributions 
129*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(waitpid(master_pid, &status, 0), NULL);
130*4f1223e8SApple OSS Distributions 	T_ASSERT_TRUE(WIFSIGNALED(status), "master PID was signaled");
131*4f1223e8SApple OSS Distributions 	(void)waitpid((int)slave_pid, &status, 0);
132*4f1223e8SApple OSS Distributions }
133*4f1223e8SApple OSS Distributions 
134*4f1223e8SApple OSS Distributions volatile static bool writing = true;
135*4f1223e8SApple OSS Distributions 
136*4f1223e8SApple OSS Distributions static void *
reader_thread(void * arg)137*4f1223e8SApple OSS Distributions reader_thread(void *arg)
138*4f1223e8SApple OSS Distributions {
139*4f1223e8SApple OSS Distributions 	int fd = (int)arg;
140*4f1223e8SApple OSS Distributions 	char c;
141*4f1223e8SApple OSS Distributions 
142*4f1223e8SApple OSS Distributions 	T_SETUPBEGIN;
143*4f1223e8SApple OSS Distributions 	T_QUIET;
144*4f1223e8SApple OSS Distributions 	T_ASSERT_GT(fd, 0, "reader thread received valid fd");
145*4f1223e8SApple OSS Distributions 	T_SETUPEND;
146*4f1223e8SApple OSS Distributions 
147*4f1223e8SApple OSS Distributions 	for (;;) {
148*4f1223e8SApple OSS Distributions 		ssize_t rdsize = read(fd, &c, sizeof(c));
149*4f1223e8SApple OSS Distributions 		if (rdsize == -1) {
150*4f1223e8SApple OSS Distributions 			if (errno == EINTR) {
151*4f1223e8SApple OSS Distributions 				continue;
152*4f1223e8SApple OSS Distributions 			} else if (errno == EBADF) {
153*4f1223e8SApple OSS Distributions 				T_LOG("reader got an error (%s), shutting down",
154*4f1223e8SApple OSS Distributions 				    strerror(errno));
155*4f1223e8SApple OSS Distributions 				return NULL;
156*4f1223e8SApple OSS Distributions 			} else {
157*4f1223e8SApple OSS Distributions 				T_ASSERT_POSIX_SUCCESS(rdsize, "read on PTY");
158*4f1223e8SApple OSS Distributions 			}
159*4f1223e8SApple OSS Distributions 		} else if (rdsize == 0) {
160*4f1223e8SApple OSS Distributions 			return NULL;
161*4f1223e8SApple OSS Distributions 		}
162*4f1223e8SApple OSS Distributions 	}
163*4f1223e8SApple OSS Distributions 
164*4f1223e8SApple OSS Distributions 	return NULL;
165*4f1223e8SApple OSS Distributions }
166*4f1223e8SApple OSS Distributions 
167*4f1223e8SApple OSS Distributions static void *
writer_thread(void * arg)168*4f1223e8SApple OSS Distributions writer_thread(void *arg)
169*4f1223e8SApple OSS Distributions {
170*4f1223e8SApple OSS Distributions 	int fd = (int)arg;
171*4f1223e8SApple OSS Distributions 	char c[4096];
172*4f1223e8SApple OSS Distributions 	memset(c, 'a', sizeof(c));
173*4f1223e8SApple OSS Distributions 
174*4f1223e8SApple OSS Distributions 	T_SETUPBEGIN;
175*4f1223e8SApple OSS Distributions 	T_QUIET;
176*4f1223e8SApple OSS Distributions 	T_ASSERT_GT(fd, 0, "writer thread received valid fd");
177*4f1223e8SApple OSS Distributions 	T_SETUPEND;
178*4f1223e8SApple OSS Distributions 
179*4f1223e8SApple OSS Distributions 	while (writing) {
180*4f1223e8SApple OSS Distributions 		ssize_t wrsize = write(fd, c, sizeof(c));
181*4f1223e8SApple OSS Distributions 		if (wrsize == -1) {
182*4f1223e8SApple OSS Distributions 			if (errno == EINTR) {
183*4f1223e8SApple OSS Distributions 				continue;
184*4f1223e8SApple OSS Distributions 			} else {
185*4f1223e8SApple OSS Distributions 				T_LOG("writer got an error (%s), shutting down",
186*4f1223e8SApple OSS Distributions 				    strerror(errno));
187*4f1223e8SApple OSS Distributions 				return NULL;
188*4f1223e8SApple OSS Distributions 			}
189*4f1223e8SApple OSS Distributions 		}
190*4f1223e8SApple OSS Distributions 	}
191*4f1223e8SApple OSS Distributions 
192*4f1223e8SApple OSS Distributions 	return NULL;
193*4f1223e8SApple OSS Distributions }
194*4f1223e8SApple OSS Distributions 
195*4f1223e8SApple OSS Distributions #define ATTACH_ITERATIONS 10000
196*4f1223e8SApple OSS Distributions 
197*4f1223e8SApple OSS Distributions static int attach_master, attach_slave;
198*4f1223e8SApple OSS Distributions static pthread_t reader, writer;
199*4f1223e8SApple OSS Distributions 
200*4f1223e8SApple OSS Distributions static void
redispatch(dispatch_group_t grp,dispatch_source_type_t type,int fd)201*4f1223e8SApple OSS Distributions redispatch(dispatch_group_t grp, dispatch_source_type_t type, int fd)
202*4f1223e8SApple OSS Distributions {
203*4f1223e8SApple OSS Distributions 	__block int iters = 0;
204*4f1223e8SApple OSS Distributions 
205*4f1223e8SApple OSS Distributions 	__block void (^redispatch_blk)(void) = Block_copy(^{
206*4f1223e8SApple OSS Distributions 		if (iters++ > ATTACH_ITERATIONS) {
207*4f1223e8SApple OSS Distributions 		        return;
208*4f1223e8SApple OSS Distributions 		} else if (iters == ATTACH_ITERATIONS) {
209*4f1223e8SApple OSS Distributions 		        dispatch_group_leave(grp);
210*4f1223e8SApple OSS Distributions 		        T_PASS("created %d %s sources on busy PTY", iters,
211*4f1223e8SApple OSS Distributions 		        type == DISPATCH_SOURCE_TYPE_READ ? "read" : "write");
212*4f1223e8SApple OSS Distributions 		}
213*4f1223e8SApple OSS Distributions 
214*4f1223e8SApple OSS Distributions 		dispatch_source_t src = dispatch_source_create(
215*4f1223e8SApple OSS Distributions 			type, (uintptr_t)fd, 0,
216*4f1223e8SApple OSS Distributions 			dispatch_get_main_queue());
217*4f1223e8SApple OSS Distributions 
218*4f1223e8SApple OSS Distributions 		dispatch_source_set_event_handler(src, ^{
219*4f1223e8SApple OSS Distributions 			dispatch_cancel(src);
220*4f1223e8SApple OSS Distributions 		});
221*4f1223e8SApple OSS Distributions 
222*4f1223e8SApple OSS Distributions 		dispatch_source_set_cancel_handler(src, redispatch_blk);
223*4f1223e8SApple OSS Distributions 
224*4f1223e8SApple OSS Distributions 		dispatch_activate(src);
225*4f1223e8SApple OSS Distributions 	});
226*4f1223e8SApple OSS Distributions 
227*4f1223e8SApple OSS Distributions 	dispatch_group_enter(grp);
228*4f1223e8SApple OSS Distributions 	dispatch_async(dispatch_get_main_queue(), redispatch_blk);
229*4f1223e8SApple OSS Distributions }
230*4f1223e8SApple OSS Distributions 
231*4f1223e8SApple OSS Distributions T_DECL(attach_while_tty_wakeups,
232*4f1223e8SApple OSS Distributions     "try to attach knotes while a TTY is getting wakeups", T_META_TAG_VM_PREFERRED)
233*4f1223e8SApple OSS Distributions {
234*4f1223e8SApple OSS Distributions 	dispatch_group_t grp = dispatch_group_create();
235*4f1223e8SApple OSS Distributions 
236*4f1223e8SApple OSS Distributions 	T_SETUPBEGIN;
237*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(openpty(&attach_master, &attach_slave, NULL, NULL,
238*4f1223e8SApple OSS Distributions 	    NULL), NULL);
239*4f1223e8SApple OSS Distributions 
240*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(pthread_create(&reader, NULL, reader_thread,
241*4f1223e8SApple OSS Distributions 	    (void *)(uintptr_t)attach_master), NULL);
242*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_ZERO(pthread_create(&writer, NULL, writer_thread,
243*4f1223e8SApple OSS Distributions 	    (void *)(uintptr_t)attach_slave), NULL);
244*4f1223e8SApple OSS Distributions 	T_SETUPEND;
245*4f1223e8SApple OSS Distributions 
246*4f1223e8SApple OSS Distributions 	redispatch(grp, DISPATCH_SOURCE_TYPE_READ, attach_master);
247*4f1223e8SApple OSS Distributions 	redispatch(grp, DISPATCH_SOURCE_TYPE_WRITE, attach_slave);
248*4f1223e8SApple OSS Distributions 
249*4f1223e8SApple OSS Distributions 	dispatch_group_notify(grp, dispatch_get_main_queue(), ^{
250*4f1223e8SApple OSS Distributions 		T_LOG("both reader and writer sources cleaned up");
251*4f1223e8SApple OSS Distributions 		T_END;
252*4f1223e8SApple OSS Distributions 	});
253*4f1223e8SApple OSS Distributions 
254*4f1223e8SApple OSS Distributions 	dispatch_main();
255*4f1223e8SApple OSS Distributions }
256*4f1223e8SApple OSS Distributions 
257*4f1223e8SApple OSS Distributions T_DECL(master_read_data_set,
258*4f1223e8SApple OSS Distributions     "check that the data is set on read sources of master fds", T_META_TAG_VM_PREFERRED)
259*4f1223e8SApple OSS Distributions {
260*4f1223e8SApple OSS Distributions 	int master = -1, slave = -1;
261*4f1223e8SApple OSS Distributions 
262*4f1223e8SApple OSS Distributions 	T_SETUPBEGIN;
263*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(openpty(&master, &slave, NULL, NULL, NULL), NULL);
264*4f1223e8SApple OSS Distributions 	T_QUIET; T_ASSERT_GE(master, 0, "master fd is valid");
265*4f1223e8SApple OSS Distributions 	T_QUIET; T_ASSERT_GE(slave, 0, "slave fd is valid");
266*4f1223e8SApple OSS Distributions 
267*4f1223e8SApple OSS Distributions 	dispatch_source_t src = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
268*4f1223e8SApple OSS Distributions 	    (uintptr_t)master, 0, dispatch_get_main_queue());
269*4f1223e8SApple OSS Distributions 
270*4f1223e8SApple OSS Distributions 	dispatch_source_set_event_handler(src, ^{
271*4f1223e8SApple OSS Distributions 		unsigned long len = dispatch_source_get_data(src);
272*4f1223e8SApple OSS Distributions 		T_EXPECT_GT(len, (unsigned long)0,
273*4f1223e8SApple OSS Distributions 		"the amount of data to read was set for the master source");
274*4f1223e8SApple OSS Distributions 		dispatch_cancel(src);
275*4f1223e8SApple OSS Distributions 	});
276*4f1223e8SApple OSS Distributions 
277*4f1223e8SApple OSS Distributions 	dispatch_source_set_cancel_handler(src, ^{
278*4f1223e8SApple OSS Distributions 		dispatch_release(src);
279*4f1223e8SApple OSS Distributions 		T_END;
280*4f1223e8SApple OSS Distributions 	});
281*4f1223e8SApple OSS Distributions 
282*4f1223e8SApple OSS Distributions 	dispatch_activate(src);
283*4f1223e8SApple OSS Distributions 	T_SETUPEND;
284*4f1223e8SApple OSS Distributions 
285*4f1223e8SApple OSS Distributions 	// Let's not fill up the TTY's buffer, otherwise write(2) will block.
286*4f1223e8SApple OSS Distributions 	char buf[512] = "";
287*4f1223e8SApple OSS Distributions 
288*4f1223e8SApple OSS Distributions 	int ret = 0;
289*4f1223e8SApple OSS Distributions 	while ((ret = write(slave, buf, sizeof(buf)) == -1 && errno == EAGAIN)) {
290*4f1223e8SApple OSS Distributions 		;
291*4f1223e8SApple OSS Distributions 	}
292*4f1223e8SApple OSS Distributions 	T_ASSERT_POSIX_SUCCESS(ret, "slave wrote data");
293*4f1223e8SApple OSS Distributions 
294*4f1223e8SApple OSS Distributions 	dispatch_main();
295*4f1223e8SApple OSS Distributions }
296