Line data Source code
1 : /**
2 : * @file pty_sync.c
3 : * @brief PTY output reading and synchronisation (poll + timeout).
4 : */
5 :
6 : #define _DEFAULT_SOURCE
7 : #define _XOPEN_SOURCE 600
8 :
9 : #include "pty_internal.h"
10 : #include <errno.h>
11 : #include <poll.h>
12 : #include <stdio.h>
13 : #include <unistd.h>
14 : #include <time.h>
15 :
16 : /* ── Time helpers ────────────────────────────────────────────────────── */
17 :
18 233569 : static long now_ms(void) {
19 : struct timespec ts;
20 233569 : clock_gettime(CLOCK_MONOTONIC, &ts);
21 233569 : return ts.tv_sec * 1000L + ts.tv_nsec / 1000000L;
22 : }
23 :
24 : /* ── Read and feed ───────────────────────────────────────────────────── */
25 :
26 : int g_trace_active = 0;
27 0 : void pty_trace_enable(int on) { g_trace_active = on; }
28 :
29 : /** @brief Reads available data from master fd and feeds to screen. */
30 1560837 : static int read_and_feed(PtySession *s) {
31 : char buf[8192];
32 1560837 : ssize_t n = read(s->master_fd, buf, sizeof(buf));
33 1560837 : if (n > 0) {
34 1560331 : if (g_trace_active) {
35 0 : printf(" [TRACE %zd bytes]:", n);
36 0 : for (ssize_t i = 0; i < n && i < 120; i++) {
37 0 : unsigned char c = (unsigned char)buf[i];
38 0 : if (c == 0x1B) printf(" ESC");
39 0 : else if (c < 0x20 || c > 0x7E) printf(" %02X", c);
40 0 : else printf(" %c", c);
41 : }
42 0 : printf("\n"); fflush(stdout);
43 : }
44 1560331 : pty_screen_feed(s->screen, buf, (size_t)n);
45 1560331 : return (int)n;
46 : }
47 : /* Return -2 to distinguish EAGAIN from EIO/EOF */
48 506 : if (n < 0 && errno == EAGAIN) return -2;
49 506 : return 0;
50 : }
51 :
52 506 : int pty_drain(PtySession *s) {
53 506 : if (!s || s->master_fd < 0) return 0;
54 506 : int total = 0;
55 : /* Retry on EAGAIN — kernel may need a moment to deliver buffered PTY data
56 : * after the slave side closes (POLLHUP race on non-blocking master fds). */
57 506 : for (int attempt = 0; attempt < 3; attempt++) {
58 0 : for (;;) {
59 506 : int n = read_and_feed(s);
60 506 : if (n > 0) { total += n; continue; }
61 506 : if (n == -2) break; /* EAGAIN: no data right now, retry after sleep */
62 506 : return total; /* EIO/EOF: slave closed and buffer empty */
63 : }
64 0 : if (total > 0) break; /* Got data — no need to retry */
65 0 : usleep(5000); /* 5 ms: give kernel time to deliver buffered data */
66 : }
67 0 : return total;
68 : }
69 :
70 : /* ── Synchronisation ─────────────────────────────────────────────────── */
71 :
72 78892 : int pty_wait_for(PtySession *s, const char *text, int timeout_ms) {
73 78892 : if (!s || s->master_fd < 0) return -1;
74 :
75 78892 : long deadline = now_ms() + timeout_ms;
76 78892 : struct pollfd pfd = { .fd = s->master_fd, .events = POLLIN };
77 :
78 154424 : for (;;) {
79 : /* Check if text is already on screen */
80 233316 : if (pty_screen_contains(s, text)) return 0;
81 :
82 154677 : long remaining = deadline - now_ms();
83 154677 : if (remaining <= 0) return -1; /* Timeout */
84 :
85 154677 : int ret = poll(&pfd, 1, (int)remaining);
86 154677 : if (ret > 0 && (pfd.revents & POLLIN)) {
87 154424 : read_and_feed(s);
88 253 : } else if (ret == 0) {
89 0 : return -1; /* Timeout */
90 : } else {
91 : /* POLLERR/POLLHUP — child may have exited; drain any buffered output */
92 253 : pty_drain(s);
93 253 : if (pty_screen_contains(s, text)) return 0;
94 : /* One more short drain in case the kernel delivered data after the HUP */
95 253 : usleep(10000);
96 253 : pty_drain(s);
97 253 : return pty_screen_contains(s, text) ? 0 : -1;
98 : }
99 : }
100 : }
101 :
102 65481 : int pty_settle(PtySession *s, int quiet_ms) {
103 65481 : if (!s || s->master_fd < 0) return -1;
104 :
105 65481 : struct pollfd pfd = { .fd = s->master_fd, .events = POLLIN };
106 :
107 1405907 : for (;;) {
108 1471388 : int ret = poll(&pfd, 1, quiet_ms);
109 1471388 : if (ret == 0) return 0; /* Quiet period elapsed — settled */
110 1411032 : if (ret > 0 && (pfd.revents & POLLIN)) {
111 1405907 : if (read_and_feed(s) <= 0) return -1; /* EOF — child exited */
112 : } else {
113 5125 : return -1; /* Error */
114 : }
115 : }
116 : }
|