Move file handling protocol stuff all into file.c so it can be reused
authornicm <nicm@openbsd.org>
Thu, 11 Feb 2021 08:28:45 +0000 (08:28 +0000)
committernicm <nicm@openbsd.org>
Thu, 11 Feb 2021 08:28:45 +0000 (08:28 +0000)
more easily.

usr.bin/tmux/client.c
usr.bin/tmux/file.c
usr.bin/tmux/server-client.c
usr.bin/tmux/tmux.h

index defce19..9ef017e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: client.c,v 1.151 2021/01/17 16:17:41 nicm Exp $ */
+/* $OpenBSD: client.c,v 1.152 2021/02/11 08:28:45 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -477,257 +477,6 @@ client_send_identify(const char *ttynam, const char *cwd, int feat)
        proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
 }
 
-/* File write error callback. */
-static void
-client_write_error_callback(__unused struct bufferevent *bev,
-    __unused short what, void *arg)
-{
-       struct client_file      *cf = arg;
-
-       log_debug("write error file %d", cf->stream);
-
-       bufferevent_free(cf->event);
-       cf->event = NULL;
-
-       close(cf->fd);
-       cf->fd = -1;
-
-       if (client_exitflag)
-               client_exit();
-}
-
-/* File write callback. */
-static void
-client_write_callback(__unused struct bufferevent *bev, void *arg)
-{
-       struct client_file      *cf = arg;
-
-       if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
-               bufferevent_free(cf->event);
-               close(cf->fd);
-               RB_REMOVE(client_files, &client_files, cf);
-               file_free(cf);
-       }
-
-       if (client_exitflag)
-               client_exit();
-}
-
-/* Open write file. */
-static void
-client_write_open(void *data, size_t datalen)
-{
-       struct msg_write_open   *msg = data;
-       const char              *path;
-       struct msg_write_ready   reply;
-       struct client_file       find, *cf;
-       const int                flags = O_NONBLOCK|O_WRONLY|O_CREAT;
-       int                      error = 0;
-
-       if (datalen < sizeof *msg)
-               fatalx("bad MSG_WRITE_OPEN size");
-       if (datalen == sizeof *msg)
-               path = "-";
-       else
-               path = (const char *)(msg + 1);
-       log_debug("open write file %d %s", msg->stream, path);
-
-       find.stream = msg->stream;
-       if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
-               cf = file_create(NULL, msg->stream, NULL, NULL);
-               RB_INSERT(client_files, &client_files, cf);
-       } else {
-               error = EBADF;
-               goto reply;
-       }
-       if (cf->closed) {
-               error = EBADF;
-               goto reply;
-       }
-
-       cf->fd = -1;
-       if (msg->fd == -1)
-               cf->fd = open(path, msg->flags|flags, 0644);
-       else {
-               if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
-                       errno = EBADF;
-               else {
-                       cf->fd = dup(msg->fd);
-                       if (~client_flags & CLIENT_CONTROL)
-                               close(msg->fd); /* can only be used once */
-               }
-       }
-       if (cf->fd == -1) {
-               error = errno;
-               goto reply;
-       }
-
-       cf->event = bufferevent_new(cf->fd, NULL, client_write_callback,
-           client_write_error_callback, cf);
-       bufferevent_enable(cf->event, EV_WRITE);
-       goto reply;
-
-reply:
-       reply.stream = msg->stream;
-       reply.error = error;
-       proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
-}
-
-/* Write to client file. */
-static void
-client_write_data(void *data, size_t datalen)
-{
-       struct msg_write_data   *msg = data;
-       struct client_file       find, *cf;
-       size_t                   size = datalen - sizeof *msg;
-
-       if (datalen < sizeof *msg)
-               fatalx("bad MSG_WRITE size");
-       find.stream = msg->stream;
-       if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
-               fatalx("unknown stream number");
-       log_debug("write %zu to file %d", size, cf->stream);
-
-       if (cf->event != NULL)
-               bufferevent_write(cf->event, msg + 1, size);
-}
-
-/* Close client file. */
-static void
-client_write_close(void *data, size_t datalen)
-{
-       struct msg_write_close  *msg = data;
-       struct client_file       find, *cf;
-
-       if (datalen != sizeof *msg)
-               fatalx("bad MSG_WRITE_CLOSE size");
-       find.stream = msg->stream;
-       if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL)
-               fatalx("unknown stream number");
-       log_debug("close file %d", cf->stream);
-
-       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, &client_files, cf);
-               file_free(cf);
-       }
-}
-
-/* File read callback. */
-static void
-client_read_callback(__unused struct bufferevent *bev, void *arg)
-{
-       struct client_file      *cf = arg;
-       void                    *bdata;
-       size_t                   bsize;
-       struct msg_read_data    *msg;
-       size_t                   msglen;
-
-       msg = xmalloc(sizeof *msg);
-       for (;;) {
-               bdata = EVBUFFER_DATA(cf->event->input);
-               bsize = EVBUFFER_LENGTH(cf->event->input);
-
-               if (bsize == 0)
-                       break;
-               if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
-                       bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
-               log_debug("read %zu from file %d", bsize, cf->stream);
-
-               msglen = (sizeof *msg) + bsize;
-               msg = xrealloc(msg, msglen);
-               msg->stream = cf->stream;
-               memcpy(msg + 1, bdata, bsize);
-               proc_send(client_peer, MSG_READ, -1, msg, msglen);
-
-               evbuffer_drain(cf->event->input, bsize);
-       }
-       free(msg);
-}
-
-/* File read error callback. */
-static void
-client_read_error_callback(__unused struct bufferevent *bev,
-    __unused short what, void *arg)
-{
-       struct client_file      *cf = arg;
-       struct msg_read_done     msg;
-
-       log_debug("read error file %d", cf->stream);
-
-       msg.stream = cf->stream;
-       msg.error = 0;
-       proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg);
-
-       bufferevent_free(cf->event);
-       close(cf->fd);
-       RB_REMOVE(client_files, &client_files, cf);
-       file_free(cf);
-}
-
-/* Open read file. */
-static void
-client_read_open(void *data, size_t datalen)
-{
-       struct msg_read_open    *msg = data;
-       const char              *path;
-       struct msg_read_done     reply;
-       struct client_file       find, *cf;
-       const int                flags = O_NONBLOCK|O_RDONLY;
-       int                      error;
-
-       if (datalen < sizeof *msg)
-               fatalx("bad MSG_READ_OPEN size");
-       if (datalen == sizeof *msg)
-               path = "-";
-       else
-               path = (const char *)(msg + 1);
-       log_debug("open read file %d %s", msg->stream, path);
-
-       find.stream = msg->stream;
-       if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) {
-               cf = file_create(NULL, msg->stream, NULL, NULL);
-               RB_INSERT(client_files, &client_files, cf);
-       } else {
-               error = EBADF;
-               goto reply;
-       }
-       if (cf->closed) {
-               error = EBADF;
-               goto reply;
-       }
-
-       cf->fd = -1;
-       if (msg->fd == -1)
-               cf->fd = open(path, flags);
-       else {
-               if (msg->fd != STDIN_FILENO)
-                       errno = EBADF;
-               else {
-                       cf->fd = dup(msg->fd);
-                       if (~client_flags & CLIENT_CONTROL)
-                               close(msg->fd); /* can only be used once */
-               }
-       }
-       if (cf->fd == -1) {
-               error = errno;
-               goto reply;
-       }
-
-       cf->event = bufferevent_new(cf->fd, client_read_callback, NULL,
-           client_read_error_callback, cf);
-       bufferevent_enable(cf->event, EV_READ);
-       return;
-
-reply:
-       reply.stream = msg->stream;
-       reply.error = error;
-       proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply);
-}
-
 /* Run command in shell; used for -c. */
 static __dead void
 client_exec(const char *shell, const char *shellcmd)
@@ -802,6 +551,16 @@ client_signal(int sig)
        }
 }
 
+/* Callback for file write error or close. */
+static void
+client_file_check_cb(__unused struct client *c, __unused const char *path,
+    __unused int error, __unused int closed, __unused struct evbuffer *buffer,
+    __unused void *data)
+{
+       if (client_exitflag)
+               client_exit();
+}
+
 /* Callback for client read events. */
 static void
 client_dispatch(struct imsg *imsg, __unused void *arg)
@@ -916,16 +675,20 @@ client_dispatch_wait(struct imsg *imsg)
                proc_exit(client_proc);
                break;
        case MSG_READ_OPEN:
-               client_read_open(data, datalen);
+               file_read_open(&client_files, client_peer, imsg, 1,
+                   !(client_flags & CLIENT_CONTROL), client_file_check_cb,
+                   NULL);
                break;
        case MSG_WRITE_OPEN:
-               client_write_open(data, datalen);
+               file_write_open(&client_files, client_peer, imsg, 1,
+                   !(client_flags & CLIENT_CONTROL), client_file_check_cb,
+                   NULL);
                break;
        case MSG_WRITE:
-               client_write_data(data, datalen);
+               file_write_data(&client_files, imsg);
                break;
        case MSG_WRITE_CLOSE:
-               client_write_close(data, datalen);
+               file_write_close(&client_files, imsg);
                break;
        case MSG_OLDSTDERR:
        case MSG_OLDSTDIN:
index 9dd877d..0520b97 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: file.c,v 1.7 2020/05/26 08:41:47 nicm Exp $ */
+/* $OpenBSD: file.c,v 1.8 2021/02/11 08:28:45 nicm Exp $ */
 
 /*
  * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
 
 #include "tmux.h"
 
+/*
+ * IPC file handling. Both client and server use the same data structures
+ * (client_file and client_files) to store list of active files. Most functions
+ * are for use either in client or server but not both.
+ */
+
 static int     file_next_stream = 3;
 
 RB_GENERATE(client_files, client_file, entry, file_cmp);
 
+/* Get path for file, either as given or from working directory. */
 static char *
 file_get_path(struct client *c, const char *file)
 {
@@ -46,6 +53,7 @@ file_get_path(struct client *c, const char *file)
        return (path);
 }
 
+/* Tree comparison function. */
 int
 file_cmp(struct client_file *cf1, struct client_file *cf2)
 {
@@ -56,11 +64,47 @@ file_cmp(struct client_file *cf1, struct client_file *cf2)
        return (0);
 }
 
+/*
+ * Create a file object in the client process - the peer is the server to send
+ * messages to. Check callback is fired when the file is finished with so the
+ * process can decide if it needs to exit (if it is waiting for files to
+ * flush).
+ */
 struct client_file *
-file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
+file_create_with_peer(struct tmuxpeer *peer, struct client_files *files,
+    int stream, client_file_cb cb, void *cbdata)
 {
        struct client_file      *cf;
 
+       cf = xcalloc(1, sizeof *cf);
+       cf->c = NULL;
+       cf->references = 1;
+       cf->stream = stream;
+
+       cf->buffer = evbuffer_new();
+       if (cf->buffer == NULL)
+               fatalx("out of memory");
+
+       cf->cb = cb;
+       cf->data = cbdata;
+
+       cf->peer = peer;
+       cf->tree = files;
+       RB_INSERT(client_files, files, cf);
+
+       return (cf);
+}
+
+/* Create a file object in the server, communicating with the given client. */
+struct client_file *
+file_create_with_client(struct client *c, int stream, client_file_cb cb,
+    void *cbdata)
+{
+       struct client_file      *cf;
+
+       if (c != NULL && (c->flags & CLIENT_ATTACHED))
+               c = NULL;
+
        cf = xcalloc(1, sizeof *cf);
        cf->c = c;
        cf->references = 1;
@@ -74,6 +118,8 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
        cf->data = cbdata;
 
        if (cf->c != NULL) {
+               cf->peer = cf->c->peer;
+               cf->tree = &cf->c->files;
                RB_INSERT(client_files, &cf->c->files, cf);
                cf->c->references++;
        }
@@ -81,6 +127,7 @@ file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
        return (cf);
 }
 
+/* Free a file. */
 void
 file_free(struct client_file *cf)
 {
@@ -90,13 +137,15 @@ file_free(struct client_file *cf)
        evbuffer_free(cf->buffer);
        free(cf->path);
 
-       if (cf->c != NULL) {
-               RB_REMOVE(client_files, &cf->c->files, cf);
+       if (cf->tree != NULL)
+               RB_REMOVE(client_files, cf->tree, cf);
+       if (cf->c != NULL)
                server_client_unref(cf->c);
-       }
+
        free(cf);
 }
 
+/* Event to fire the done callback. */
 static void
 file_fire_done_cb(__unused int fd, __unused short events, void *arg)
 {
@@ -108,21 +157,22 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
        file_free(cf);
 }
 
+/* Add an event to fire the done callback (used by the server). */
 void
 file_fire_done(struct client_file *cf)
 {
        event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
 }
 
+/* Fire the read callback. */
 void
 file_fire_read(struct client_file *cf)
 {
-       struct client   *c = cf->c;
-
        if (cf->cb != NULL)
-               cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
+               cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data);
 }
 
+/* Can this file be printed to? */
 int
 file_can_print(struct client *c)
 {
@@ -133,6 +183,7 @@ file_can_print(struct client *c)
        return (1);
 }
 
+/* Print a message to a file. */
 void
 file_print(struct client *c, const char *fmt, ...)
 {
@@ -143,6 +194,7 @@ file_print(struct client *c, const char *fmt, ...)
        va_end(ap);
 }
 
+/* Print a message to a file. */
 void
 file_vprint(struct client *c, const char *fmt, va_list ap)
 {
@@ -154,7 +206,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
 
        find.stream = 1;
        if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
-               cf = file_create(c, 1, NULL, NULL);
+               cf = file_create_with_client(c, 1, NULL, NULL);
                cf->path = xstrdup("-");
 
                evbuffer_add_vprintf(cf->buffer, fmt, ap);
@@ -169,6 +221,7 @@ file_vprint(struct client *c, const char *fmt, va_list ap)
        }
 }
 
+/* Print a buffer to a file. */
 void
 file_print_buffer(struct client *c, void *data, size_t size)
 {
@@ -180,7 +233,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
 
        find.stream = 1;
        if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
-               cf = file_create(c, 1, NULL, NULL);
+               cf = file_create_with_client(c, 1, NULL, NULL);
                cf->path = xstrdup("-");
 
                evbuffer_add(cf->buffer, data, size);
@@ -195,6 +248,7 @@ file_print_buffer(struct client *c, void *data, size_t size)
        }
 }
 
+/* Report an error to a file. */
 void
 file_error(struct client *c, const char *fmt, ...)
 {
@@ -209,7 +263,7 @@ file_error(struct client *c, const char *fmt, ...)
 
        find.stream = 2;
        if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
-               cf = file_create(c, 2, NULL, NULL);
+               cf = file_create_with_client(c, 2, NULL, NULL);
                cf->path = xstrdup("-");
 
                evbuffer_add_vprintf(cf->buffer, fmt, ap);
@@ -226,19 +280,21 @@ file_error(struct client *c, const char *fmt, ...)
        va_end(ap);
 }
 
+/* Write data to a file. */
 void
 file_write(struct client *c, const char *path, int flags, const void *bdata,
     size_t bsize, client_file_cb cb, void *cbdata)
 {
        struct client_file      *cf;
-       FILE                    *f;
        struct msg_write_open   *msg;
        size_t                   msglen;
        int                      fd = -1;
+       u_int                    stream = file_next_stream++;
+       FILE                    *f;
        const char              *mode;
 
        if (strcmp(path, "-") == 0) {
-               cf = file_create(c, file_next_stream++, cb, cbdata);
+               cf = file_create_with_client(c, stream, cb, cbdata);
                cf->path = xstrdup("-");
 
                fd = STDOUT_FILENO;
@@ -251,7 +307,7 @@ file_write(struct client *c, const char *path, int flags, const void *bdata,
                goto skip;
        }
 
-       cf = file_create(c, file_next_stream++, cb, cbdata);
+       cf = file_create_with_client(c, stream, cb, cbdata);
        cf->path = file_get_path(c, path);
 
        if (c == NULL || c->flags & CLIENT_ATTACHED) {
@@ -286,7 +342,7 @@ skip:
        msg->fd = fd;
        msg->flags = flags;
        memcpy(msg + 1, cf->path, msglen - sizeof *msg);
-       if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
+       if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
                free(msg);
                cf->error = EINVAL;
                goto done;
@@ -298,18 +354,21 @@ done:
        file_fire_done(cf);
 }
 
+/* Read a file. */
 void
 file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
 {
        struct client_file      *cf;
-       FILE                    *f;
        struct msg_read_open    *msg;
-       size_t                   msglen, size;
+       size_t                   msglen;
        int                      fd = -1;
+       u_int                    stream = file_next_stream++;
+       FILE                    *f;
+       size_t                   size;
        char                     buffer[BUFSIZ];
 
        if (strcmp(path, "-") == 0) {
-               cf = file_create(c, file_next_stream++, cb, cbdata);
+               cf = file_create_with_client(c, stream, cb, cbdata);
                cf->path = xstrdup("-");
 
                fd = STDIN_FILENO;
@@ -322,7 +381,7 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
                goto skip;
        }
 
-       cf = file_create(c, file_next_stream++, cb, cbdata);
+       cf = file_create_with_client(c, stream, cb, cbdata);
        cf->path = file_get_path(c, path);
 
        if (c == NULL || c->flags & CLIENT_ATTACHED) {
@@ -358,7 +417,7 @@ skip:
        msg->stream = cf->stream;
        msg->fd = fd;
        memcpy(msg + 1, cf->path, msglen - sizeof *msg);
-       if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
+       if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
                free(msg);
                cf->error = EINVAL;
                goto done;
@@ -370,21 +429,21 @@ done:
        file_fire_done(cf);
 }
 
+/* Push event, fired if there is more writing to be done. */
 static void
 file_push_cb(__unused int fd, __unused short events, void *arg)
 {
        struct client_file      *cf = arg;
-       struct client           *c = cf->c;
 
-       if (~c->flags & CLIENT_DEAD)
+       if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
                file_push(cf);
        file_free(cf);
 }
 
+/* Push uwritten data to the client for a file, if it will accept it. */
 void
 file_push(struct client_file *cf)
 {
-       struct client           *c = cf->c;
        struct msg_write_data   *msg;
        size_t                   msglen, sent, left;
        struct msg_write_close   close;
@@ -400,21 +459,344 @@ file_push(struct client_file *cf)
                msg = xrealloc(msg, msglen);
                msg->stream = cf->stream;
                memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
-               if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
+               if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
                        break;
                evbuffer_drain(cf->buffer, sent);
 
                left = EVBUFFER_LENGTH(cf->buffer);
-               log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
-                   sent, left);
+               log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
        }
        if (left != 0) {
                cf->references++;
                event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
        } else if (cf->stream > 2) {
                close.stream = cf->stream;
-               proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
+               proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
                file_fire_done(cf);
        }
        free(msg);
 }
+
+/* Client file write error callback. */
+static void
+file_write_error_callback(__unused struct bufferevent *bev, __unused short what,
+    void *arg)
+{
+       struct client_file      *cf = arg;
+
+       log_debug("write error file %d", cf->stream);
+
+       if (cf->cb != NULL)
+               cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
+
+       bufferevent_free(cf->event);
+       cf->event = NULL;
+
+       close(cf->fd);
+       cf->fd = -1;
+}
+
+/* Client file write callback. */
+static void
+file_write_callback(__unused struct bufferevent *bev, void *arg)
+{
+       struct client_file      *cf = arg;
+
+       log_debug("write check file %d", cf->stream);
+
+       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). */
+void
+file_write_open(struct client_files *files, struct tmuxpeer *peer,
+    struct imsg *imsg, int allow_streams, int close_received,
+    client_file_cb cb, void *cbdata)
+{
+       struct msg_write_open   *msg = imsg->data;
+       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       const char              *path;
+       struct msg_write_ready   reply;
+       struct client_file       find, *cf;
+       const int                flags = O_NONBLOCK|O_WRONLY|O_CREAT;
+       int                      error = 0;
+
+       if (msglen < sizeof *msg)
+               fatalx("bad MSG_WRITE_OPEN size");
+       if (msglen == sizeof *msg)
+               path = "-";
+       else
+               path = (const char *)(msg + 1);
+       log_debug("open write file %d %s", msg->stream, path);
+
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
+               error = EBADF;
+               goto reply;
+       }
+       cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
+       if (cf->closed) {
+               error = EBADF;
+               goto reply;
+       }
+
+       cf->fd = -1;
+       if (msg->fd == -1)
+               cf->fd = open(path, msg->flags|flags, 0644);
+       else if (allow_streams) {
+               if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
+                       errno = EBADF;
+               else {
+                       cf->fd = dup(msg->fd);
+                       if (close_received)
+                               close(msg->fd); /* can only be used once */
+               }
+       } else
+             errno = EBADF;
+       if (cf->fd == -1) {
+               error = errno;
+               goto reply;
+       }
+
+       cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
+           file_write_error_callback, cf);
+       bufferevent_enable(cf->event, EV_WRITE);
+       goto reply;
+
+reply:
+       reply.stream = msg->stream;
+       reply.error = error;
+       proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
+}
+
+/* Handle a file write data message (client). */
+void
+file_write_data(struct client_files *files, struct imsg *imsg)
+{
+       struct msg_write_data   *msg = imsg->data;
+       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       struct client_file       find, *cf;
+       size_t                   size = msglen - sizeof *msg;
+
+       if (msglen < sizeof *msg)
+               fatalx("bad MSG_WRITE size");
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+               fatalx("unknown stream number");
+       log_debug("write %zu to file %d", size, cf->stream);
+
+       if (cf->event != NULL)
+               bufferevent_write(cf->event, msg + 1, size);
+}
+
+/* Handle a file write close message (client). */
+void
+file_write_close(struct client_files *files, struct imsg *imsg)
+{
+       struct msg_write_close  *msg = imsg->data;
+       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       struct client_file       find, *cf;
+
+       if (msglen != sizeof *msg)
+               fatalx("bad MSG_WRITE_CLOSE size");
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+               fatalx("unknown stream number");
+       log_debug("close file %d", cf->stream);
+
+       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);
+       }
+}
+
+/* Client file read error callback. */
+static void
+file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
+    void *arg)
+{
+       struct client_file      *cf = arg;
+       struct msg_read_done     msg;
+
+       log_debug("read error file %d", cf->stream);
+
+       msg.stream = cf->stream;
+       msg.error = 0;
+       proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
+
+       bufferevent_free(cf->event);
+       close(cf->fd);
+       RB_REMOVE(client_files, cf->tree, cf);
+       file_free(cf);
+}
+
+/* Client file read callback. */
+static void
+file_read_callback(__unused struct bufferevent *bev, void *arg)
+{
+       struct client_file      *cf = arg;
+       void                    *bdata;
+       size_t                   bsize;
+       struct msg_read_data    *msg;
+       size_t                   msglen;
+
+       msg = xmalloc(sizeof *msg);
+       for (;;) {
+               bdata = EVBUFFER_DATA(cf->event->input);
+               bsize = EVBUFFER_LENGTH(cf->event->input);
+
+               if (bsize == 0)
+                       break;
+               if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
+                       bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
+               log_debug("read %zu from file %d", bsize, cf->stream);
+
+               msglen = (sizeof *msg) + bsize;
+               msg = xrealloc(msg, msglen);
+               msg->stream = cf->stream;
+               memcpy(msg + 1, bdata, bsize);
+               proc_send(cf->peer, MSG_READ, -1, msg, msglen);
+
+               evbuffer_drain(cf->event->input, bsize);
+       }
+       free(msg);
+}
+
+/* Handle a file read open message (client). */
+void
+file_read_open(struct client_files *files, struct tmuxpeer *peer,
+    struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
+    void *cbdata)
+{
+       struct msg_read_open    *msg = imsg->data;
+       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       const char              *path;
+       struct msg_read_done     reply;
+       struct client_file       find, *cf;
+       const int                flags = O_NONBLOCK|O_RDONLY;
+       int                      error;
+
+       if (msglen < sizeof *msg)
+               fatalx("bad MSG_READ_OPEN size");
+       if (msglen == sizeof *msg)
+               path = "-";
+       else
+               path = (const char *)(msg + 1);
+       log_debug("open read file %d %s", msg->stream, path);
+
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) != NULL) {
+               error = EBADF;
+               goto reply;
+       }
+       cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
+       if (cf->closed) {
+               error = EBADF;
+               goto reply;
+       }
+
+       cf->fd = -1;
+       if (msg->fd == -1)
+               cf->fd = open(path, flags);
+       else if (allow_streams) {
+               if (msg->fd != STDIN_FILENO)
+                       errno = EBADF;
+               else {
+                       cf->fd = dup(msg->fd);
+                       if (close_received)
+                               close(msg->fd); /* can only be used once */
+               }
+       } else
+               errno = EBADF;
+       if (cf->fd == -1) {
+               error = errno;
+               goto reply;
+       }
+
+       cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
+           file_read_error_callback, cf);
+       bufferevent_enable(cf->event, EV_READ);
+       return;
+
+reply:
+       reply.stream = msg->stream;
+       reply.error = error;
+       proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
+}
+
+/* Handle a write ready message (server). */
+void
+file_write_ready(struct client_files *files, struct imsg *imsg)
+{
+       struct msg_write_ready  *msg = imsg->data;
+       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       struct client_file       find, *cf;
+
+       if (msglen != sizeof *msg)
+               fatalx("bad MSG_WRITE_READY size");
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+               return;
+       if (msg->error != 0) {
+               cf->error = msg->error;
+               file_fire_done(cf);
+       } else
+               file_push(cf);
+}
+
+/* Handle read data message (server). */
+void
+file_read_data(struct client_files *files, struct imsg *imsg)
+{
+       struct msg_read_data    *msg = imsg->data;
+       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
+       struct client_file       find, *cf;
+       void                    *bdata = msg + 1;
+       size_t                   bsize = msglen - sizeof *msg;
+
+       if (msglen < sizeof *msg)
+               fatalx("bad MSG_READ_DATA size");
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+               return;
+
+       log_debug("file %d read %zu bytes", cf->stream, bsize);
+       if (cf->error == 0) {
+               if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
+                       cf->error = ENOMEM;
+                       file_fire_done(cf);
+               } else
+                       file_fire_read(cf);
+       }
+}
+
+/* Handle a read done message (server). */
+void
+file_read_done(struct client_files *files, struct imsg *imsg)
+{
+       struct msg_read_done    *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_DONE size");
+       find.stream = msg->stream;
+       if ((cf = RB_FIND(client_files, files, &find)) == NULL)
+               return;
+
+       log_debug("file %d read done", cf->stream);
+       cf->error = msg->error;
+       file_fire_done(cf);
+}
index 9d5b3d6..0a1245c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: server-client.c,v 1.368 2021/02/08 08:33:54 nicm Exp $ */
+/* $OpenBSD: server-client.c,v 1.369 2021/02/11 08:28:45 nicm Exp $ */
 
 /*
  * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -51,12 +51,6 @@ static void  server_client_dispatch(struct imsg *, void *);
 static void    server_client_dispatch_command(struct client *, struct imsg *);
 static void    server_client_dispatch_identify(struct client *, struct imsg *);
 static void    server_client_dispatch_shell(struct client *);
-static void    server_client_dispatch_write_ready(struct client *,
-                   struct imsg *);
-static void    server_client_dispatch_read_data(struct client *,
-                   struct imsg *);
-static void    server_client_dispatch_read_done(struct client *,
-                   struct imsg *);
 
 /* Compare client windows. */
 static int
@@ -2070,13 +2064,13 @@ server_client_dispatch(struct imsg *imsg, void *arg)
                server_client_dispatch_shell(c);
                break;
        case MSG_WRITE_READY:
-               server_client_dispatch_write_ready(c, imsg);
+               file_write_ready(&c->files, imsg);
                break;
        case MSG_READ:
-               server_client_dispatch_read_data(c, imsg);
+               file_read_data(&c->files, imsg);
                break;
        case MSG_READ_DONE:
-               server_client_dispatch_read_done(c, imsg);
+               file_read_done(&c->files, imsg);
                break;
        }
 }
@@ -2302,71 +2296,6 @@ server_client_dispatch_shell(struct client *c)
        proc_kill_peer(c->peer);
 }
 
-/* Handle write ready message. */
-static void
-server_client_dispatch_write_ready(struct client *c, struct imsg *imsg)
-{
-       struct msg_write_ready  *msg = imsg->data;
-       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
-       struct client_file       find, *cf;
-
-       if (msglen != sizeof *msg)
-               fatalx("bad MSG_WRITE_READY size");
-       find.stream = msg->stream;
-       if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
-               return;
-       if (msg->error != 0) {
-               cf->error = msg->error;
-               file_fire_done(cf);
-       } else
-               file_push(cf);
-}
-
-/* Handle read data message. */
-static void
-server_client_dispatch_read_data(struct client *c, struct imsg *imsg)
-{
-       struct msg_read_data    *msg = imsg->data;
-       size_t                   msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
-       struct client_file       find, *cf;
-       void                    *bdata = msg + 1;
-       size_t                   bsize = msglen - sizeof *msg;
-
-       if (msglen < sizeof *msg)
-               fatalx("bad MSG_READ_DATA size");
-       find.stream = msg->stream;
-       if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
-               return;
-
-       log_debug("%s: file %d read %zu bytes", c->name, cf->stream, bsize);
-       if (cf->error == 0) {
-               if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
-                       cf->error = ENOMEM;
-                       file_fire_done(cf);
-               } else
-                       file_fire_read(cf);
-       }
-}
-
-/* Handle read done message. */
-static void
-server_client_dispatch_read_done(struct client *c, struct imsg *imsg)
-{
-       struct msg_read_done    *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_DONE size");
-       find.stream = msg->stream;
-       if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL)
-               return;
-
-       log_debug("%s: file %d read done", c->name, cf->stream);
-       cf->error = msg->error;
-       file_fire_done(cf);
-}
-
 /* Get client working directory. */
 const char *
 server_client_get_cwd(struct client *c, struct session *s)
index 3ad7d9d..243ac4b 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: tmux.h,v 1.1088 2021/02/05 12:29:19 nicm Exp $ */
+/* $OpenBSD: tmux.h,v 1.1089 2021/02/11 08:28:45 nicm Exp $ */
 
 /*
  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -1539,6 +1539,8 @@ typedef void (*client_file_cb) (struct client *, const char *, int, int,
     struct evbuffer *, void *);
 struct client_file {
        struct client                   *c;
+       struct tmuxpeer                 *peer;
+       struct client_files             *tree;
        int                              references;
        int                              stream;
 
@@ -1907,6 +1909,7 @@ int       load_cfg(const char *, struct client *, struct cmdq_item *, int,
 int    load_cfg_from_buffer(const void *, size_t, const char *,
            struct client *, struct cmdq_item *, int, struct cmdq_item **);
 void   set_cfg_file(const char *);
+const char *get_cfg_file(void);
 void printflike(1, 2) cfg_add_cause(const char *, ...);
 void   cfg_print_causes(struct cmdq_item *);
 void   cfg_show_causes(struct session *);
@@ -2370,7 +2373,10 @@ void     alerts_check_session(struct session *);
 /* file.c */
 int     file_cmp(struct client_file *, struct client_file *);
 RB_PROTOTYPE(client_files, client_file, entry, file_cmp);
-struct client_file *file_create(struct client *, int, client_file_cb, void *);
+struct client_file *file_create_with_peer(struct tmuxpeer *,
+           struct client_files *, int, client_file_cb, void *);
+struct client_file *file_create_with_client(struct client *, int,
+           client_file_cb, void *);
 void    file_free(struct client_file *);
 void    file_fire_done(struct client_file *);
 void    file_fire_read(struct client_file *);
@@ -2383,6 +2389,15 @@ 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 *);
 void    file_push(struct client_file *);
+void    file_write_open(struct client_files *, struct tmuxpeer *,
+            struct imsg *, int, int, client_file_cb, void *);
+void    file_write_data(struct client_files *, struct imsg *);
+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 *);
+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 *);
 
 /* server.c */
 extern struct tmuxproc *server_proc;