3 #endif /* T_NAMESPACE */
6 #include <darwintest.h>
7 #include <dispatch/dispatch.h>
19 T_META_NAMESPACE("xnu.kevent"),
20 T_META_CHECK_LEAKS(false),
21 T_META_RUN_CONCURRENTLY(true));
23 #define TIMEOUT_SECS 10
25 static int child_ready
[2];
28 child_tty_client(void)
30 dispatch_source_t src
;
34 src
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
,
35 (uintptr_t)STDIN_FILENO
, 0, NULL
);
39 dispatch_source_set_event_handler(src
, ^{});
41 dispatch_activate(src
);
43 close(child_ready
[0]);
44 snprintf(buf
, sizeof(buf
), "%ds", getpid());
45 bytes_wr
= write(child_ready
[1], buf
, strlen(buf
));
47 err(1, "failed to write on child ready pipe");
63 ret
= setpgid(child_pid
, child_pid
);
67 ret
= tcsetpgrp(STDIN_FILENO
, child_pid
);
76 T_DECL(pty_master_teardown
,
77 "try removing a TTY master out from under a PTY slave holding a kevent",
80 __block pid_t master_pid
;
85 unsigned long slave_pid
;
87 char pty_filename
[PATH_MAX
];
91 T_ASSERT_POSIX_SUCCESS(pipe(child_ready
), NULL
);
93 master_pid
= forkpty(&master_fd
, pty_filename
, NULL
, NULL
);
94 if (master_pid
== 0) {
96 __builtin_unreachable();
98 T_ASSERT_POSIX_SUCCESS(master_pid
,
99 "forked child master PTY with pid %d, at pty %s", master_pid
,
102 close(child_ready
[1]);
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
);
111 } while (bytes_rd
!= 0 && *(end
- 1) != 's');
113 slave_pid
= strtoul(buf
, &end
, 0);
115 T_ASSERT_FAIL("could not parse child PID from master pipe");
118 T_LOG("got pid %lu for slave process from master", slave_pid
);
121 T_LOG("sending fatal signal to master");
122 T_ASSERT_POSIX_SUCCESS(kill(master_pid
, SIGKILL
), NULL
);
124 T_LOG("sending fatal signal to slave");
125 (void)kill((int)slave_pid
, SIGKILL
);
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);
132 volatile static bool writing
= true;
135 reader_thread(void *arg
)
142 T_ASSERT_GT(fd
, 0, "reader thread received valid fd");
146 ssize_t rdsize
= read(fd
, &c
, sizeof(c
));
148 if (errno
== EINTR
) {
150 } else if (errno
== EBADF
) {
151 T_LOG("reader got an error (%s), shutting down",
155 T_ASSERT_POSIX_SUCCESS(rdsize
, "read on PTY");
157 } else if (rdsize
== 0) {
166 writer_thread(void *arg
)
170 memset(c
, 'a', sizeof(c
));
174 T_ASSERT_GT(fd
, 0, "writer thread received valid fd");
178 ssize_t wrsize
= write(fd
, c
, sizeof(c
));
180 if (errno
== EINTR
) {
183 T_LOG("writer got an error (%s), shutting down",
193 #define ATTACH_ITERATIONS 10000
195 static int attach_master
, attach_slave
;
196 static pthread_t reader
, writer
;
199 redispatch(dispatch_group_t grp
, dispatch_source_type_t type
, int fd
)
201 __block
int iters
= 0;
203 __block
void (^redispatch_blk
)(void) = Block_copy(^{
204 if (iters
++ > ATTACH_ITERATIONS
) {
206 } else if (iters
== ATTACH_ITERATIONS
) {
207 dispatch_group_leave(grp
);
208 T_PASS("created %d %s sources on busy PTY", iters
,
209 type
== DISPATCH_SOURCE_TYPE_READ
? "read" : "write");
212 dispatch_source_t src
= dispatch_source_create(
213 type
, (uintptr_t)fd
, 0,
214 dispatch_get_main_queue());
216 dispatch_source_set_event_handler(src
, ^{
217 dispatch_cancel(src
);
220 dispatch_source_set_cancel_handler(src
, redispatch_blk
);
222 dispatch_activate(src
);
225 dispatch_group_enter(grp
);
226 dispatch_async(dispatch_get_main_queue(), redispatch_blk
);
229 T_DECL(attach_while_tty_wakeups
,
230 "try to attach knotes while a TTY is getting wakeups")
232 dispatch_group_t grp
= dispatch_group_create();
235 T_ASSERT_POSIX_SUCCESS(openpty(&attach_master
, &attach_slave
, NULL
, NULL
,
238 T_ASSERT_POSIX_ZERO(pthread_create(&reader
, NULL
, reader_thread
,
239 (void *)(uintptr_t)attach_master
), NULL
);
240 T_ASSERT_POSIX_ZERO(pthread_create(&writer
, NULL
, writer_thread
,
241 (void *)(uintptr_t)attach_slave
), NULL
);
244 redispatch(grp
, DISPATCH_SOURCE_TYPE_READ
, attach_master
);
245 redispatch(grp
, DISPATCH_SOURCE_TYPE_WRITE
, attach_slave
);
247 dispatch_group_notify(grp
, dispatch_get_main_queue(), ^{
248 T_LOG("both reader and writer sources cleaned up");
255 T_DECL(master_read_data_set
,
256 "check that the data is set on read sources of master fds")
258 int master
= -1, slave
= -1;
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");
265 dispatch_source_t src
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
,
266 (uintptr_t)master
, 0, dispatch_get_main_queue());
268 dispatch_source_set_event_handler(src
, ^{
269 unsigned long len
= dispatch_source_get_data(src
);
270 T_EXPECT_GT(len
, (unsigned long)0,
271 "the amount of data to read was set for the master source");
272 dispatch_cancel(src
);
275 dispatch_source_set_cancel_handler(src
, ^{
276 dispatch_release(src
);
280 dispatch_activate(src
);
283 // Let's not fill up the TTY's buffer, otherwise write(2) will block.
287 while ((ret
= write(slave
, buf
, sizeof(buf
)) == -1 && errno
== EAGAIN
)) {
290 T_ASSERT_POSIX_SUCCESS(ret
, "slave wrote data");