From 5cd1976a4a5bbefc9391d2e3febc4ca4d74ebede Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 4 Aug 2026 11:18:22 +0000 Subject: [PATCH] Do not attempt to drain write buffer on stuck clients, since it requires a weird dance to make libevent do it. Instead, just ignore the client and destroy the buffer normally if not drained in 10 seconds. --- control.c | 10 +++------- server-client.c | 41 +++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/control.c b/control.c index 036a25882..7f76ff9e3 100644 --- a/control.c +++ b/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.64 2026/08/03 20:18:20 nicm Exp $ */ +/* $OpenBSD: control.c,v 1.65 2026/08/04 11:18:22 nicm Exp $ */ /* * Copyright (c) 2012 Nicholas Marriott @@ -968,21 +968,17 @@ control_discard(struct client *c) bufferevent_disable(cs->read_event, EV_READ); } -/* - * Discard all output for a client, including output which has already been - * queued to be written. - */ +/* Discard all tmux-owned queued control blocks and stop writing. */ void control_discard_all(struct client *c) { struct control_state *cs = c->control_state; struct control_block *cb, *cb1; - struct evbuffer *evb = cs->write_event->output; control_discard(c); TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1) control_free_block(cs, cb); - evbuffer_drain(evb, EVBUFFER_LENGTH(evb)); + bufferevent_disable(cs->write_event, EV_WRITE); } /* Stop control mode. */ diff --git a/server-client.c b/server-client.c index a7270e88f..27b7688fe 100644 --- a/server-client.c +++ b/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.501 2026/08/03 20:18:20 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.502 2026/08/04 11:18:22 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -41,7 +41,7 @@ static void server_client_check_window_resize(struct window *); static key_code server_client_check_mouse(struct client *, struct key_event *); static void server_client_repeat_timer(int, short, void *); static void server_client_click_timer(int, short, void *); -static void server_client_check_exit(struct client *); +static void server_client_check_exit(struct client *, int); static void server_client_exit_timer(int, short, void *); static void server_client_check_redraw(struct client *); static void server_client_check_modes(struct client *); @@ -1857,7 +1857,7 @@ server_client_loop(void) /* Check clients. */ TAILQ_FOREACH(c, &clients, entry) { - server_client_check_exit(c); + server_client_check_exit(c, 0); if (c->session != NULL && c->session->curw != NULL) { server_client_check_modes(c); server_client_check_redraw(c); @@ -2307,8 +2307,7 @@ server_client_start_exit_timer(struct client *c) static void server_client_exit_timer(__unused int fd, __unused short events, void *data) { - struct client *c = data; - struct client_file *cf; + struct client *c = data; if (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) return; @@ -2318,17 +2317,13 @@ server_client_exit_timer(__unused int fd, __unused short events, void *data) server_client_lost(c); } else if (c->flags & CLIENT_EXIT) { log_debug("%s: %s took too long to flush", __func__, c->name); - if (c->flags & CLIENT_CONTROL) - control_discard_all(c); - RB_FOREACH(cf, client_files, &c->files) - evbuffer_drain(cf->buffer, EVBUFFER_LENGTH(cf->buffer)); - server_client_check_exit(c); + server_client_check_exit(c, 1); } } -/* Check if client should be exited. */ +/* Check if client should be exited, abandoning buffered output if forced. */ static void -server_client_check_exit(struct client *c) +server_client_check_exit(struct client *c, int force) { struct client_file *cf; const char *name = c->exit_session; @@ -2341,16 +2336,22 @@ server_client_check_exit(struct client *c) return; if (c->flags & CLIENT_CONTROL) { - control_discard(c); - if (!control_all_done(c)) { - server_client_start_exit_timer(c); - return; + if (force) + control_discard_all(c); + else { + control_discard(c); + if (!control_all_done(c)) { + server_client_start_exit_timer(c); + return; + } } } - RB_FOREACH(cf, client_files, &c->files) { - if (EVBUFFER_LENGTH(cf->buffer) != 0) { - server_client_start_exit_timer(c); - return; + if (!force) { + RB_FOREACH(cf, client_files, &c->files) { + if (EVBUFFER_LENGTH(cf->buffer) != 0) { + server_client_start_exit_timer(c); + return; + } } } c->flags |= CLIENT_EXITED;