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