From 80e6621298d8c05584ca8dbe90eadad2d54d5a54 Mon Sep 17 00:00:00 2001 From: Rafael Kitover <rkitover@gmail.com> Date: Thu, 25 Jul 2024 21:55:40 +0000 Subject: [PATCH] Pass terminal data on socket on Cygwin platforms On Cygwin and MSYS2 use a controlling pty for the server client handler instead of passing a file descriptor over the socket which is not possible on Cygwin platforms. Attach an event to STDIN on the client, passing data from it to the server and attach an event to the pty out_fd on the server client handler and pass data from it to the client. Signed-off-by: Rafael Kitover <rkitover@gmail.com> --- client.c | 14 ++++++++++++++ compat.h | 5 +++++ server-client.c | 12 ++++++++++-- tmux-protocol.h | 5 +++++ tmux.h | 5 +++++ tty.c | 39 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 78 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 26e48df3..9f6b02b9 100644 --- a/client.c +++ b/client.c @@ -244,6 +244,9 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, char *line = NULL, **caps = NULL, *cause; u_int ncaps = 0; struct args_value *values; +#ifdef TTY_OVER_SOCKET + struct tty* tty = xmalloc(sizeof *tty); +#endif /* Set up the initial command. */ if (shell_command != NULL) { @@ -397,6 +400,10 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, } else if (msg == MSG_SHELL) proc_send(client_peer, msg, -1, NULL, 0); +#ifdef TTY_OVER_SOCKET + tty_attach_stdin_to_socket(tty, client_peer); +#endif + /* Start main loop. */ proc_loop(client_proc, NULL); @@ -474,12 +481,14 @@ client_send_identify(const char *ttynam, const char *termname, char **caps, caps[i], strlen(caps[i]) + 1); } +#ifndef TTY_OVER_SOCKET if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); if ((fd = dup(STDOUT_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDOUT, fd, NULL, 0); +#endif pid = getpid(); proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); @@ -805,5 +814,10 @@ client_dispatch_attached(struct imsg *imsg) system(data); proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); break; +#ifdef TTY_OVER_SOCKET + case MSG_TTY_READ: + write(STDOUT_FILENO, data, datalen); + break; +#endif } } diff --git a/compat.h b/compat.h index f33dcf1a..00fe1b12 100644 --- a/compat.h +++ b/compat.h @@ -69,6 +69,11 @@ #define __weak __attribute__ ((__weak__)) #endif +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__) +#define WIN32_PLATFORM +#define TTY_OVER_SOCKET +#endif + #ifndef ECHOPRT #define ECHOPRT 0 #endif diff --git a/server-client.c b/server-client.c index 24ea04fe..87e2a09a 100644 --- a/server-client.c +++ b/server-client.c @@ -22,6 +22,7 @@ #include <errno.h> #include <fcntl.h> + #include <stdlib.h> #include <string.h> #include <time.h> @@ -2898,6 +2899,11 @@ server_client_dispatch(struct imsg *imsg, void *arg) case MSG_READ_DONE: file_read_done(&c->files, imsg); break; +#ifdef TTY_OVER_SOCKET + case MSG_TTY_WRITE: + write(c->fd, imsg->data, datalen); + break; +#endif } } @@ -3069,6 +3075,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->cwd = xstrdup("/"); log_debug("client %p IDENTIFY_CWD %s", c, data); break; +#ifndef TTY_OVER_SOCKET case MSG_IDENTIFY_STDIN: if (datalen != 0) fatalx("bad MSG_IDENTIFY_STDIN size"); @@ -3081,6 +3088,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->out_fd = imsg_get_fd(imsg); log_debug("client %p IDENTIFY_STDOUT %d", c, c->out_fd); break; +#endif case MSG_IDENTIFY_ENVIRON: if (datalen == 0 || data[datalen - 1] != '\0') fatalx("bad MSG_IDENTIFY_ENVIRON string"); @@ -3109,8 +3117,8 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg) c->name = name; log_debug("client %p name is %s", c, c->name); -#ifdef __CYGWIN__ - c->fd = open(c->ttyname, O_RDWR|O_NOCTTY); +#ifdef TTY_OVER_SOCKET + c->fd = open("/dev/ptmx", O_RDWR|O_NOCTTY); c->out_fd = dup(c->fd); #endif diff --git a/tmux-protocol.h b/tmux-protocol.h index 3cf00c09..443b817d 100644 --- a/tmux-protocol.h +++ b/tmux-protocol.h @@ -68,6 +68,11 @@ enum msgtype { MSG_WRITE_READY, MSG_WRITE_CLOSE, MSG_READ_CANCEL +#ifdef TTY_OVER_SOCKET + , + MSG_TTY_READ, + MSG_TTY_WRITE +#endif }; /* diff --git a/tmux.h b/tmux.h index dfc81cee..3df49023 100644 --- a/tmux.h +++ b/tmux.h @@ -2461,6 +2461,11 @@ void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *); void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); void tty_default_colours(struct grid_cell *, struct window_pane *); +#ifdef TTY_OVER_SOCKET +int tty_attach_stdin_to_socket(struct tty *, struct tmuxpeer *); +int tty_attach_out_to_socket(struct tty *, struct client *); +#endif + /* tty-term.c */ extern struct tty_terms tty_terms; u_int tty_term_ncodes(void); diff --git a/tty.c b/tty.c index 2c9b7299..b6e582ad 100644 --- a/tty.c +++ b/tty.c @@ -250,6 +250,11 @@ tty_write_callback(__unused int fd, __unused short events, void *data) struct client *c = tty->client; size_t size = EVBUFFER_LENGTH(tty->out); int nwrite; +#ifdef TTY_OVER_SOCKET + enum msgtype msg = MSG_TTY_READ; + + proc_send(c->peer, msg, -1, EVBUFFER_DATA(tty->out), size); +#endif nwrite = evbuffer_write(tty->out, c->fd); if (nwrite == -1) @@ -304,6 +309,40 @@ tty_open(struct tty *tty, char **cause) return (0); } +#ifdef TTY_OVER_SOCKET +static struct tmuxpeer *client_peer; + +static void +tty_stdin_socket_callback(int fd, __unused short events, void *data) +{ + struct tty *tty = data; + int nread; + enum msgtype msg = MSG_TTY_WRITE; + + nread = evbuffer_read(tty->in, fd, -1); + if (nread == 0 || nread == -1) { + event_del(&tty->event_in); + return; + } + + proc_send(client_peer, msg, -1, EVBUFFER_DATA(tty->in), nread); +} + +int +tty_attach_stdin_to_socket(struct tty *tty, struct tmuxpeer *peer) +{ + client_peer = peer; + + event_set(&tty->event_in, STDIN_FILENO, EV_PERSIST|EV_READ, + tty_stdin_socket_callback, tty); + tty->in = evbuffer_new(); + if (tty->in == NULL) + fatal("out of memory"); + + return (0); +} +#endif + static void tty_start_timer_callback(__unused int fd, __unused short events, void *data) {