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