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));
22 #define TIMEOUT_SECS 10
24 static int child_ready
[2];
27 child_tty_client(void)
29 dispatch_source_t src
;
33 src
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
,
34 (uintptr_t)STDIN_FILENO
, 0, NULL
);
38 dispatch_source_set_event_handler(src
, ^{});
40 dispatch_activate(src
);
42 close(child_ready
[0]);
43 snprintf(buf
, sizeof(buf
), "%ds", getpid());
44 bytes_wr
= write(child_ready
[1], buf
, strlen(buf
));
46 err(1, "failed to write on child ready pipe");
62 ret
= setpgid(child_pid
, child_pid
);
66 ret
= tcsetpgrp(STDIN_FILENO
, child_pid
);
75 T_DECL(pty_master_teardown
,
76 "try removing a TTY master out from under a PTY slave holding a kevent",
79 __block pid_t master_pid
;
84 unsigned long slave_pid
;
86 char pty_filename
[PATH_MAX
];
90 T_ASSERT_POSIX_SUCCESS(pipe(child_ready
), NULL
);
92 master_pid
= forkpty(&master_fd
, pty_filename
, NULL
, NULL
);
93 if (master_pid
== 0) {
95 __builtin_unreachable();
97 T_ASSERT_POSIX_SUCCESS(master_pid
,
98 "forked child master PTY with pid %d, at pty %s", master_pid
,
101 close(child_ready
[1]);
105 bytes_rd
= read(child_ready
[0], end
, sizeof(buf
) - buf_len
);
106 T_ASSERT_POSIX_SUCCESS(bytes_rd
, "read on pipe between master and runner");
107 buf_len
+= (size_t)bytes_rd
;
108 T_LOG("runner read %zd bytes", bytes_rd
);
110 } while (bytes_rd
!= 0 && *(end
- 1) != 's');
112 slave_pid
= strtoul(buf
, &end
, 0);
114 T_ASSERT_FAIL("could not parse child PID from master pipe");
117 T_LOG("got pid %lu for slave process from master", slave_pid
);
120 T_LOG("sending fatal signal to master");
121 T_ASSERT_POSIX_SUCCESS(kill(master_pid
, SIGKILL
), NULL
);
123 T_LOG("sending fatal signal to slave");
124 (void)kill((int)slave_pid
, SIGKILL
);
126 T_ASSERT_POSIX_SUCCESS(waitpid(master_pid
, &status
, 0), NULL
);
127 T_ASSERT_TRUE(WIFSIGNALED(status
), "master PID was signaled");
128 (void)waitpid((int)slave_pid
, &status
, 0);
131 volatile static bool writing
= true;
134 reader_thread(void *arg
)
141 T_ASSERT_GT(fd
, 0, "reader thread received valid fd");
145 ssize_t rdsize
= read(fd
, &c
, sizeof(c
));
147 if (errno
== EINTR
) {
149 } else if (errno
== EBADF
) {
150 T_LOG("reader got an error (%s), shutting down", strerror(errno
));
153 T_ASSERT_POSIX_SUCCESS(rdsize
, "read on PTY");
155 } else if (rdsize
== 0) {
164 writer_thread(void *arg
)
168 memset(c
, 'a', sizeof(c
));
172 T_ASSERT_GT(fd
, 0, "writer thread received valid fd");
176 ssize_t wrsize
= write(fd
, c
, sizeof(c
));
178 if (errno
== EINTR
) {
181 T_LOG("writer got an error (%s), shutting down", strerror(errno
));
190 #define ATTACH_ITERATIONS 10000
192 static int attach_master
, attach_slave
;
193 static pthread_t reader
, writer
;
199 close(attach_master
);
201 pthread_join(reader
, NULL
);
202 pthread_join(writer
, NULL
);
206 redispatch(dispatch_group_t grp
, dispatch_source_type_t type
, int fd
)
208 __block
int iters
= 0;
210 __block
void (^redispatch_blk
)(void) = Block_copy(^{
211 if (iters
++ > ATTACH_ITERATIONS
) {
213 } else if (iters
== ATTACH_ITERATIONS
) {
214 dispatch_group_leave(grp
);
215 T_PASS("created %d %s sources on busy PTY", iters
,
216 type
== DISPATCH_SOURCE_TYPE_READ
? "read" : "write");
219 dispatch_source_t src
= dispatch_source_create(
220 type
, (uintptr_t)fd
, 0,
221 dispatch_get_main_queue());
223 dispatch_source_set_event_handler(src
, ^{
224 dispatch_cancel(src
);
227 dispatch_source_set_cancel_handler(src
, redispatch_blk
);
229 dispatch_activate(src
);
232 dispatch_group_enter(grp
);
233 dispatch_async(dispatch_get_main_queue(), redispatch_blk
);
236 T_DECL(attach_while_tty_wakeups
,
237 "try to attach knotes while a TTY is getting wakeups")
239 dispatch_group_t grp
= dispatch_group_create();
242 T_ASSERT_POSIX_SUCCESS(openpty(&attach_master
, &attach_slave
, NULL
, NULL
,
245 T_ASSERT_POSIX_ZERO(pthread_create(&reader
, NULL
, reader_thread
,
246 (void *)(uintptr_t)attach_master
), NULL
);
247 T_ASSERT_POSIX_ZERO(pthread_create(&writer
, NULL
, writer_thread
,
248 (void *)(uintptr_t)attach_slave
), NULL
);
249 T_ATEND(join_threads
);
252 redispatch(grp
, DISPATCH_SOURCE_TYPE_READ
, attach_master
);
253 redispatch(grp
, DISPATCH_SOURCE_TYPE_WRITE
, attach_slave
);
255 dispatch_group_notify(grp
, dispatch_get_main_queue(), ^{
256 T_LOG("both reader and writer sources cleaned up");
263 T_DECL(master_read_data_set
,
264 "check that the data is set on read sources of master fds")
266 int master
= -1, slave
= -1;
269 T_ASSERT_POSIX_SUCCESS(openpty(&master
, &slave
, NULL
, NULL
, NULL
), NULL
);
270 T_QUIET
; T_ASSERT_GE(master
, 0, "master fd is valid");
271 T_QUIET
; T_ASSERT_GE(slave
, 0, "slave fd is valid");
273 dispatch_source_t src
= dispatch_source_create(DISPATCH_SOURCE_TYPE_READ
,
274 (uintptr_t)master
, 0, dispatch_get_main_queue());
276 dispatch_source_set_event_handler(src
, ^{
277 unsigned long len
= dispatch_source_get_data(src
);
278 T_EXPECT_GT(len
, (unsigned long)0,
279 "the amount of data to read was set for the master source");
280 dispatch_cancel(src
);
283 dispatch_source_set_cancel_handler(src
, ^{
284 dispatch_release(src
);
288 dispatch_activate(src
);
291 // Let's not fill up the TTY's buffer, otherwise write(2) will block.
295 while ((ret
= write(slave
, buf
, sizeof(buf
)) == -1 && errno
== EAGAIN
)) {
298 T_ASSERT_POSIX_SUCCESS(ret
, "slave wrote data");