diff --git a/client.c b/client.c index f413dc819..a59216823 100644 --- a/client.c +++ b/client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: client.c,v 1.166 2026/07/10 15:45:11 nicm Exp $ */ +/* $OpenBSD: client.c,v 1.167 2026/08/17 07:56:56 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -277,7 +277,7 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, proc_set_signals(client_proc, client_signal); /* Save the flags. */ - client_flags = flags; + client_flags = flags|CLIENT_WRITE_ACK; log_debug("flags are %#llx", (unsigned long long)client_flags); /* Initialize the client socket and start the server. */ diff --git a/file.c b/file.c index 4207f8053..fd73f98df 100644 --- a/file.c +++ b/file.c @@ -1,4 +1,4 @@ -/* $OpenBSD: file.c,v 1.21 2026/07/26 15:08:15 nicm Exp $ */ +/* $OpenBSD: file.c,v 1.22 2026/08/17 07:56:56 nicm Exp $ */ /* * Copyright (c) 2019 Nicholas Marriott @@ -505,7 +505,8 @@ file_push(struct client_file *cf) } else if (cf->stream > 2) { close.stream = cf->stream; proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close); - file_fire_done(cf); + if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK)) + file_fire_done(cf); } free(msg); } @@ -530,14 +531,48 @@ file_write_left(struct client_files *files) return (waiting != 0); } +/* Finish writing a client file. */ +static void +file_write_finished(struct client_file *cf) +{ + struct msg_write_done msg; + + if (cf->event != NULL) { + bufferevent_free(cf->event); + cf->event = NULL; + } + if (cf->fd != -1) { + if (close(cf->fd) != 0 && cf->error == 0) + cf->error = errno; + cf->fd = -1; + } + + msg.stream = cf->stream; + msg.error = cf->error; + proc_send(cf->peer, MSG_WRITE_DONE, -1, &msg, sizeof msg); + + if (cf->cb != NULL) + cf->cb(NULL, NULL, 0, -1, NULL, cf->data); + file_free(cf); +} + /* Client file write error callback. */ static void -file_write_error_callback(__unused struct bufferevent *bev, __unused short what, +file_write_error_callback(__unused struct bufferevent *bev, short what, void *arg) { struct client_file *cf = arg; + int error; + + if (what & EVBUFFER_ERROR) + error = errno; + else + error = EIO; + if (error == 0) + error = EIO; log_debug("write error file %d", cf->stream); + cf->error = error; bufferevent_free(cf->event); cf->event = NULL; @@ -545,7 +580,9 @@ file_write_error_callback(__unused struct bufferevent *bev, __unused short what, close(cf->fd); cf->fd = -1; - if (cf->cb != NULL) + if (cf->closed) + file_write_finished(cf); + else if (cf->cb != NULL) cf->cb(NULL, NULL, 0, -1, NULL, cf->data); } @@ -557,15 +594,10 @@ file_write_callback(__unused struct bufferevent *bev, void *arg) log_debug("write check file %d", cf->stream); - if (cf->cb != NULL) + if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) + file_write_finished(cf); + else if (cf->cb != NULL) cf->cb(NULL, NULL, 0, -1, NULL, cf->data); - - if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { - bufferevent_free(cf->event); - close(cf->fd); - RB_REMOVE(client_files, cf->tree, cf); - file_free(cf); - } } /* Handle a file write open message (client). */ @@ -666,14 +698,10 @@ file_write_close(struct client_files *files, struct imsg *imsg) if ((cf = RB_FIND(client_files, files, &find)) == NULL) fatalx("unknown stream number"); log_debug("close file %d", cf->stream); + cf->closed = 1; if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { - if (cf->event != NULL) - bufferevent_free(cf->event); - if (cf->fd != -1) - close(cf->fd); - RB_REMOVE(client_files, files, cf); - file_free(cf); + file_write_finished(cf); } } @@ -832,6 +860,28 @@ file_write_ready(struct client_files *files, struct imsg *imsg) return (0); } +/* Handle a write done message (server). */ +int +file_write_done(struct client_files *files, struct imsg *imsg) +{ + struct msg_write_done *msg = imsg->data; + size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; + struct client_file find, *cf; + + if (msglen != sizeof *msg) + return (-1); + find.stream = msg->stream; + if ((cf = RB_FIND(client_files, files, &find)) == NULL) + return (0); + if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK)) + return (0); + + log_debug("file %d write done", cf->stream); + cf->error = msg->error; + file_fire_done(cf); + return (0); +} + /* Handle read data message (server). */ int file_read_data(struct client_files *files, struct imsg *imsg) diff --git a/server-client.c b/server-client.c index 27b7688fe..4fe4917ad 100644 --- a/server-client.c +++ b/server-client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: server-client.c,v 1.502 2026/08/04 11:18:22 nicm Exp $ */ +/* $OpenBSD: server-client.c,v 1.503 2026/08/17 07:56:56 nicm Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -2700,6 +2700,10 @@ server_client_dispatch(struct imsg *imsg, void *arg) if (file_write_ready(&c->files, imsg) != 0) goto bad; break; + case MSG_WRITE_DONE: + if (file_write_done(&c->files, imsg) != 0) + goto bad; + break; case MSG_READ: if (file_read_data(&c->files, imsg) != 0) goto bad; diff --git a/tmux-protocol.h b/tmux-protocol.h index d823ce7e2..396f10bc9 100644 --- a/tmux-protocol.h +++ b/tmux-protocol.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux-protocol.h,v 1.2 2023/01/06 07:09:27 nicm Exp $ */ +/* $OpenBSD: tmux-protocol.h,v 1.3 2026/08/17 07:56:56 nicm Exp $ */ /* * Copyright (c) 2021 Nicholas Marriott @@ -67,7 +67,8 @@ enum msgtype { MSG_WRITE, MSG_WRITE_READY, MSG_WRITE_CLOSE, - MSG_READ_CANCEL + MSG_READ_CANCEL, + MSG_WRITE_DONE }; /* @@ -116,4 +117,9 @@ struct msg_write_close { int stream; }; +struct msg_write_done { + int stream; + int error; +}; + #endif /* TMUX_PROTOCOL_H */ diff --git a/tmux.h b/tmux.h index 15b997b76..666f901e4 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1422 2026/08/05 08:54:56 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1423 2026/08/17 07:56:56 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -2252,7 +2252,7 @@ struct client { /* 0x800000000ULL unused */ #define CLIENT_BRACKETPASTING 0x1000000000ULL #define CLIENT_ASSUMEPASTING 0x2000000000ULL -/* 0x4000000000ULL unused */ +#define CLIENT_WRITE_ACK 0x4000000000ULL #define CLIENT_NO_DETACH_ON_DESTROY 0x8000000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ @@ -3235,6 +3235,7 @@ void file_write_close(struct client_files *, struct imsg *); void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *, int, int, client_file_cb, void *); int file_write_ready(struct client_files *, struct imsg *); +int file_write_done(struct client_files *, struct imsg *); int file_read_data(struct client_files *, struct imsg *); int file_read_done(struct client_files *, struct imsg *); void file_read_cancel(struct client_files *, struct imsg *);