If a pane is killed, cancel reading from the file. GitHub issue 3422.
authornicm <nicm@openbsd.org>
Fri, 6 Jan 2023 07:09:27 +0000 (07:09 +0000)
committernicm <nicm@openbsd.org>
Fri, 6 Jan 2023 07:09:27 +0000 (07:09 +0000)
usr.bin/tmux/client.c
usr.bin/tmux/file.c
usr.bin/tmux/tmux-protocol.h
usr.bin/tmux/tmux.h
usr.bin/tmux/window.c

index 509476b..b5c4d36 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: client.c,v 1.158 2022/05/30 12:48:57 nicm Exp $ */
+/* $OpenBSD: client.c,v 1.159 2023/01/06 07:09:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -693,6 +693,9 @@ client_dispatch_wait(struct imsg *imsg)
                    !(client_flags & CLIENT_CONTROL), client_file_check_cb,
                    NULL);
                break;
+       case MSG_READ_CANCEL:
+               file_read_cancel(&client_files, imsg);
+               break;
        case MSG_WRITE_OPEN:
                file_write_open(&client_files, client_peer, imsg, 1,
                    !(client_flags & CLIENT_CONTROL), client_file_check_cb,
index 336f004..c993f39 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: file.c,v 1.13 2022/08/24 07:22:30 nicm Exp $ */
+/* $OpenBSD: file.c,v 1.14 2023/01/06 07:09:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -152,7 +152,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
        struct client_file      *cf = arg;
        struct client           *c = cf->c;
 
-       if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
+       if (cf->cb != NULL &&
+           (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
                cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
        file_free(cf);
 }
@@ -355,7 +356,7 @@ done:
 }
 
 /* Read a file. */
-void
+struct client_file *
 file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
 {
        struct client_file      *cf;
@@ -423,10 +424,27 @@ skip:
                goto done;
        }
        free(msg);
-       return;
+       return cf;
 
 done:
        file_fire_done(cf);
+       return NULL;
+}
+
+/* Cancel a file read. */
+void
+file_cancel(struct client_file *cf)
+{
+       struct msg_read_cancel   msg;
+
+       log_debug("read cancel file %d", cf->stream);
+
+       if (cf->closed)
+               return;
+       cf->closed = 1;
+
+       msg.stream = cf->stream;
+       proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
 }
 
 /* Push event, fired if there is more writing to be done. */
@@ -760,6 +778,24 @@ reply:
        proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
 }
 
+/* Handle a read cancel message (client). */
+void
+file_read_cancel(struct client_files *files, struct imsg *imsg)
+{
+       struct msg_read_cancel  *msg = imsg->data;
+       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       struct client_file       find, *cf;
+
+       if (msglen != sizeof *msg)
+               fatalx("bad MSG_READ_CANCEL size");
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+               fatalx("unknown stream number");
+       log_debug("cancel file %d", cf->stream);
+
+       file_read_error_callback(NULL, 0, cf);
+}
+
 /* Handle a write ready message (server). */
 void
 file_write_ready(struct client_files *files, struct imsg *imsg)
@@ -797,7 +833,7 @@ file_read_data(struct client_files *files, struct imsg *imsg)
                return;
 
        log_debug("file %d read %zu bytes", cf->stream, bsize);
-       if (cf->error == 0) {
+       if (cf->error == 0 && !cf->closed) {
                if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
                        cf->error = ENOMEM;
                        file_fire_done(cf);
index df324cd..d823ce7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux-protocol.h,v 1.1 2021/08/13 07:37:58 nicm Exp $ */
+/* $OpenBSD: tmux-protocol.h,v 1.2 2023/01/06 07:09:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2021 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -66,7 +66,8 @@ enum msgtype {
        MSG_WRITE_OPEN,
        MSG_WRITE,
        MSG_WRITE_READY,
-       MSG_WRITE_CLOSE
+       MSG_WRITE_CLOSE,
+       MSG_READ_CANCEL
 };
 
 /*
@@ -92,6 +93,10 @@ struct msg_read_done {
        int     error;
 };
 
+struct msg_read_cancel {
+       int     stream;
+};
+
 struct msg_write_open {
        int     stream;
        int     fd;
index f3b5766..3c027a8 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1189 2023/01/03 11:43:24 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1190 2023/01/06 07:09:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -2611,7 +2611,9 @@ void       file_print_buffer(struct client *, void *, size_t);
 void printflike(2, 3) file_error(struct client *, const char *, ...);
 void    file_write(struct client *, const char *, int, const void *, size_t,
             client_file_cb, void *);
-void    file_read(struct client *, const char *, client_file_cb, void *);
+struct client_file *file_read(struct client *, const char *, client_file_cb,
+            void *);
+void    file_cancel(struct client_file *);
 void    file_push(struct client_file *);
 int     file_write_left(struct client_files *);
 void    file_write_open(struct client_files *, struct tmuxpeer *,
@@ -2623,6 +2625,7 @@ void       file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
 void    file_write_ready(struct client_files *, struct imsg *);
 void    file_read_data(struct client_files *, struct imsg *);
 void    file_read_done(struct client_files *, struct imsg *);
+void    file_read_cancel(struct client_files *, struct imsg *);
 
 /* server.c */
 extern struct tmuxproc *server_proc;
index c1edcdd..218613c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: window.c,v 1.282 2022/08/24 07:22:30 nicm Exp $ */
+/* $OpenBSD: window.c,v 1.283 2023/01/06 07:09:27 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -66,6 +66,7 @@ static u_int  next_active_point;
 struct window_pane_input_data {
        struct cmdq_item        *item;
        u_int                    wp;
+       struct client_file      *file;
 };
 
 static struct window_pane *window_pane_create(struct window *, u_int, u_int,
@@ -1533,18 +1534,16 @@ window_pane_input_callback(struct client *c, __unused const char *path,
        size_t                           len = EVBUFFER_LENGTH(buffer);
 
        wp = window_pane_find_by_id(cdata->wp);
-       if (wp == NULL || closed || error != 0 || (c->flags & CLIENT_DEAD)) {
+       if (cdata->file != NULL && (wp == NULL || c->flags & CLIENT_DEAD)) {
                if (wp == NULL)
                        c->flags |= CLIENT_EXIT;
-
-               evbuffer_drain(buffer, len);
+               file_cancel(cdata->file);
+       } else if (cdata->file == NULL || closed || error != 0) {
                cmdq_continue(cdata->item);
-
                server_client_unref(c);
                free(cdata);
-               return;
-       }
-       input_parse_buffer(wp, buf, len);
+       } else
+               input_parse_buffer(wp, buf, len);
        evbuffer_drain(buffer, len);
 }
 
@@ -1567,9 +1566,8 @@ window_pane_start_input(struct window_pane *wp, struct cmdq_item *item,
        cdata = xmalloc(sizeof *cdata);
        cdata->item = item;
        cdata->wp = wp->id;
-
+       cdata->file = file_read(c, "-", window_pane_input_callback, cdata);
        c->references++;
-       file_read(c, "-", window_pane_input_callback, cdata);
 
        return (0);
 }