The Pedigree Project 0.1
epoll-pty.c
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#define _GNU_SOURCE
9
10#include <errno.h>
11#include <fcntl.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <termios.h>
15#include <unistd.h>
16
17#include <sys/epoll.h>
18#include <sys/ioctl.h>
19
20extern void fail(void) __attribute__((noreturn));
21
22static void fail_operation(const char* operation) {
23 printf("\nepoll PTY: %s failed (errno=%d)\n", operation, errno);
24 fail();
25}
26
27static void expect_read_edges(int writer, int reader, char first, char second) {
28 int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
29 if (epoll_fd < 0)
30 fail_operation("create read epoll");
31
32 struct epoll_event requested = {.events = EPOLLIN | EPOLLET, .data.u64 = 0x505459};
33 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, reader, &requested))
34 fail_operation("register reader");
35
36 struct epoll_event result = {0};
37 if (epoll_wait(epoll_fd, &result, 1, 0) != 0)
38 fail_operation("initial read readiness");
39
40 if (write(writer, &first, 1) != 1 || epoll_wait(epoll_fd, &result, 1, 1000) != 1 ||
41 !(result.events & EPOLLIN) || result.data.u64 != requested.data.u64)
42 fail_operation("first write/readiness");
43
44 char received = 0;
45 if (read(reader, &received, 1) != 1 || received != first ||
46 epoll_wait(epoll_fd, &result, 1, 0) != 0)
47 fail_operation("first read/drain");
48
49 if (write(writer, &second, 1) != 1 || epoll_wait(epoll_fd, &result, 1, 1000) != 1 ||
50 !(result.events & EPOLLIN))
51 fail_operation("second write/readiness");
52 received = 0;
53 if (read(reader, &received, 1) != 1 || received != second)
54 fail_operation("second read");
55
56 close(epoll_fd);
57}
58
59static void expect_writable(int descriptor) {
60 int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
61 if (epoll_fd < 0)
62 fail_operation("create write epoll");
63
64 struct epoll_event requested = {.events = EPOLLOUT, .data.fd = descriptor};
65 struct epoll_event result = {0};
66 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, descriptor, &requested) ||
67 epoll_wait(epoll_fd, &result, 1, 0) != 1 || !(result.events & EPOLLOUT) ||
68 result.data.fd != descriptor)
69 fail_operation("write readiness");
70
71 close(epoll_fd);
72}
73
74void test_epoll_pty(void) {
75 printf("Testing epoll PTY readiness...");
76 fflush(stdout);
77
78 int master = posix_openpt(O_RDWR | O_NONBLOCK | O_NOCTTY);
79 unsigned int terminal = 0;
80 if (master < 0 || ioctl(master, TIOCGPTN, &terminal))
81 fail_operation("open master");
82
83 char path[64];
84 if (snprintf(path, sizeof(path), "/dev/pts/%u", terminal) >= (int)sizeof(path))
85 fail_operation("slave path");
86 int slave = open(path, O_RDWR | O_NONBLOCK | O_NOCTTY);
87 if (slave < 0)
88 fail_operation("open slave");
89
90 struct termios attributes;
91 if (tcgetattr(slave, &attributes))
92 fail_operation("tcgetattr");
93 cfmakeraw(&attributes);
94 if (tcsetattr(slave, TCSANOW, &attributes))
95 fail_operation("tcsetattr");
96
97 expect_writable(master);
98 expect_writable(slave);
99 expect_read_edges(slave, master, 'a', '\n');
100 expect_read_edges(master, slave, 'c', 'd');
101
102 close(slave);
103 close(master);
104 printf("OK\n");
105}
Definition waits.c:9