]>
Commit | Line | Data |
---|---|---|
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 | ||
18 | T_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 | ||
25 | static int child_ready[2]; | |
26 | ||
27 | static void | |
28 | child_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 | ||
53 | static void | |
54 | pty_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 | ||
76 | T_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 | ||
132 | volatile static bool writing = true; | |
133 | ||
134 | static void * | |
135 | reader_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 | ||
165 | static void * | |
166 | writer_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 | 195 | static int attach_master, attach_slave; |
5ba3f43e A |
196 | static pthread_t reader, writer; |
197 | ||
5ba3f43e A |
198 | static void |
199 | redispatch(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 | ||
229 | T_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 | |
255 | T_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 | } |