Do not make pty fds blocking again until all the data has been consumed

or control mode clients can get stuck, GitHub issue 5356 from Ben Maurer.
This commit is contained in:
nicm
2026-07-10 15:45:11 +00:00
parent f3c6b4f1a3
commit 0cc84b8cae
3 changed files with 60 additions and 17 deletions

View File

@@ -19,7 +19,9 @@
#include <sys/types.h>
#include <errno.h>
#include <event.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -486,6 +488,53 @@ control_all_done(struct client *c)
return (EVBUFFER_LENGTH(cs->write_event->output) == 0);
}
/*
* Wait for the terminal to send an empty line or close, used by a control
* client after printing %exit so a wrapping terminal (such as iTerm2) can
* finish reading.
*/
void
control_wait_exit(int fd)
{
struct pollfd pfd;
struct evbuffer *evb;
char *line;
int n;
evb = evbuffer_new();
if (evb == NULL)
fatalx("out of memory");
for (;;) {
line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
if (line != NULL) {
if (*line == '\0') { /* empty line, stop */
free(line);
break;
}
free(line);
continue; /* drain buffered lines first */
}
memset(&pfd, 0, sizeof pfd);
pfd.fd = fd;
pfd.events = POLLIN;
if (poll(&pfd, 1, INFTIM) == -1) {
if (errno == EINTR)
continue;
break;
}
n = evbuffer_read(evb, fd, -1);
if (n == 0)
break;
if (n == -1 && errno != EAGAIN && errno != EINTR)
break;
}
evbuffer_free(evb);
}
/* Flush all blocks until output. */
static void
control_flush_all_blocks(struct client *c)