From dbc4ecc92d064190023c1e0171f5bb7d103ac998 Mon Sep 17 00:00:00 2001 From: Nicolas Viennot Date: Wed, 5 Jun 2013 17:54:07 -0400 Subject: [PATCH] Slave implementation draft --- .gitignore | 4 + Makefile.am | 64 +++++----- client.c | 13 ++- cmd-paste-buffer.c | 4 + cmd-pipe-pane.c | 4 + cmd-respawn-pane.c | 4 + cmd-respawn-window.c | 4 + cmd-send-keys.c | 4 + cmd-server-info.c | 4 +- cmd-set-option.c | 2 + cmd-split-window.c | 8 +- cmd.c | 4 + configure.ac | 3 + create_keys.sh | 13 +++ format.c | 4 + input-keys.c | 4 + input.c | 62 +++++----- log.c | 14 ++- names.c | 18 +++ paste.c | 2 + resize.c | 7 ++ screen-redraw.c | 1 + server-client.c | 18 ++- server-fn.c | 4 +- server.c | 13 ++- session.c | 5 +- tmate-debug.c | 76 ++++++++++++ tmate-decoder.c | 260 +++++++++++++++++++++++++++++++++++++++++ tmate-encoder.c | 39 +++++++ tmate-server.c | 164 ++++++++++++++++++++++++++ tmate-ssh-client-pty.c | 143 +++++++++++++++++++++++ tmate-ssh-client.c | 103 ++++++++++++++++ tmate-ssh-server.c | 225 +++++++++++++++++++++++++++++++++++ tmate.1 | 1 + tmate.h | 110 +++++++++++++++++ tmux.h | 11 ++ tty-keys.c | 17 +-- tty.c | 1 + window-copy.c | 4 + window.c | 27 ++++- 40 files changed, 1369 insertions(+), 99 deletions(-) create mode 100755 create_keys.sh create mode 100644 tmate-debug.c create mode 100644 tmate-decoder.c create mode 100644 tmate-encoder.c create mode 100644 tmate-server.c create mode 100644 tmate-ssh-client-pty.c create mode 100644 tmate-ssh-client.c create mode 100644 tmate-ssh-server.c create mode 120000 tmate.1 create mode 100644 tmate.h diff --git a/.gitignore b/.gitignore index c3906ada..06b9396d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ tmux Makefile Makefile.in configure +keys/ +tmate-server +cscope.* +tags diff --git a/Makefile.am b/Makefile.am index 2ce54b1a..94097eac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,8 +1,8 @@ # $Id$ # Obvious program stuff. -bin_PROGRAMS = tmux -dist_man1_MANS = tmux.1 +bin_PROGRAMS = tmate-server +dist_man1_MANS = tmate.1 # Distribution tarball options. EXTRA_DIST = \ @@ -21,6 +21,9 @@ if IS_GLIBC CFLAGS += -D_GNU_SOURCE endif +CFLAGS += -Wno-unused-parameter -Wno-unused-variable + + # Set flags for gcc. gcc4 whines abouts silly stuff so it needs slightly # different flags. if IS_GCC @@ -57,7 +60,7 @@ CFLAGS += -erroff=E_EMPTY_DECLARATION endif # List of sources. -dist_tmux_SOURCES = \ +dist_tmate_server_SOURCES = \ arguments.c \ attributes.c \ cfg.c \ @@ -171,7 +174,13 @@ dist_tmux_SOURCES = \ session.c \ signal.c \ status.c \ - tmux.c \ + tmate-debug.c \ + tmate-decoder.c \ + tmate-encoder.c \ + tmate-server.c \ + tmate-ssh-client-pty.c \ + tmate-ssh-client.c \ + tmate-ssh-server.c \ tty-acs.c \ tty-keys.c \ tty-term.c \ @@ -183,66 +192,51 @@ dist_tmux_SOURCES = \ window.c \ xmalloc.c \ xterm-keys.c -nodist_tmux_SOURCES = osdep-@PLATFORM@.c +nodist_tmate_server_SOURCES = osdep-@PLATFORM@.c # Pile in all the compat/ stuff that is needed. if NO_FORKPTY -nodist_tmux_SOURCES += compat/forkpty-@PLATFORM@.c +nodist_tmate_server_SOURCES += compat/forkpty-@PLATFORM@.c endif if NO_IMSG -nodist_tmux_SOURCES += compat/imsg.c compat/imsg-buffer.c +nodist_tmate_server_SOURCES += compat/imsg.c compat/imsg-buffer.c endif if NO_CLOSEFROM -nodist_tmux_SOURCES += compat/closefrom.c +nodist_tmate_server_SOURCES += compat/closefrom.c endif if NO_DAEMON -nodist_tmux_SOURCES += compat/daemon.c +nodist_tmate_server_SOURCES += compat/daemon.c endif if NO_SETENV -nodist_tmux_SOURCES += compat/setenv.c +nodist_tmate_server_SOURCES += compat/setenv.c endif if NO_STRLCAT -nodist_tmux_SOURCES += compat/strlcat.c +nodist_tmate_server_SOURCES += compat/strlcat.c endif if NO_STRLCPY -nodist_tmux_SOURCES += compat/strlcpy.c +nodist_tmate_server_SOURCES += compat/strlcpy.c endif if NO_ASPRINTF -nodist_tmux_SOURCES += compat/asprintf.c +nodist_tmate_server_SOURCES += compat/asprintf.c endif if NO_FGETLN -nodist_tmux_SOURCES += compat/fgetln.c +nodist_tmate_server_SOURCES += compat/fgetln.c endif if NO_GETOPT -nodist_tmux_SOURCES += compat/getopt.c +nodist_tmate_server_SOURCES += compat/getopt.c endif if NO_STRCASESTR -nodist_tmux_SOURCES += compat/strcasestr.c +nodist_tmate_server_SOURCES += compat/strcasestr.c endif if NO_STRSEP -nodist_tmux_SOURCES += compat/strsep.c +nodist_tmate_server_SOURCES += compat/strsep.c endif if NO_VIS -nodist_tmux_SOURCES += compat/vis.c compat/unvis.c +nodist_tmate_server_SOURCES += compat/vis.c compat/unvis.c endif if NO_STRTONUM -nodist_tmux_SOURCES += compat/strtonum.c +nodist_tmate_server_SOURCES += compat/strtonum.c endif if NO_B64_NTOP -nodist_tmux_SOURCES += compat/b64_ntop.c +nodist_tmate_server_SOURCES += compat/b64_ntop.c endif - -# Update SF web site. -upload-index.html: update-index.html - scp www/index.html www/main.css www/images/*.png \ - ${USER},tmux@web.sf.net:/home/groups/t/tm/tmux/htdocs - rm -f www/index.html www/images/small-* - -update-index.html: - (cd www/images && \ - rm -f small-* && \ - for i in *.png; do \ - convert "$$i" -resize 200x150 "small-$$i"; \ - done \ - ) - sed "s/%%VERSION%%/${VERSION}/g" www/index.html.in >www/index.html diff --git a/client.c b/client.c index 91f47650..f037163a 100644 --- a/client.c +++ b/client.c @@ -205,6 +205,10 @@ client_main(int argc, char **argv, int flags) cmd_list_free(cmdlist); } +#ifdef TMATE_SLAVE + cmdflags &= ~CMD_STARTSERVER; +#endif + /* * Check if this could be a nested session, if the command can't nest: * if the socket path matches $TMUX, this is probably the same server. @@ -228,7 +232,6 @@ client_main(int argc, char **argv, int flags) #ifdef HAVE_SETPROCTITLE setproctitle("client (%s)", socket_path); #endif - logfile("client"); /* Create imsg. */ imsg_init(&client_ibuf, fd); @@ -505,7 +508,7 @@ client_dispatch_wait(void *data) return (0); datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - log_debug("got %d from server", imsg.hdr.type); + log_debug2("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_EXIT: case MSG_SHUTDOWN: @@ -544,7 +547,7 @@ client_dispatch_wait(void *data) fatalx("bad MSG_STDERR"); memcpy(&stderrdata, imsg.data, sizeof stderrdata); - client_write(STDERR_FILENO, stderrdata.data, stderrdata.size); + client_write(fileno(stderr), stderrdata.data, stderrdata.size); break; case MSG_VERSION: if (datalen != 0) @@ -565,7 +568,7 @@ client_dispatch_wait(void *data) clear_signals(0); - shell_exec(shelldata.shell, shellcmd); + exit(1); /* NOTREACHED */ case MSG_DETACH: client_write_server(MSG_EXITING, NULL, 0); @@ -597,7 +600,7 @@ client_dispatch_attached(void) return (0); datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - log_debug("got %d from server", imsg.hdr.type); + log_debug2("got %d from server", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_DETACHKILL: case MSG_DETACH: diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index b07c9faf..c4202e74 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -45,6 +45,9 @@ const struct cmd_entry cmd_paste_buffer_entry = { enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE_SLAVE + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct window_pane *wp; struct session *s; @@ -99,4 +102,5 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq) } return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index aa72c699..c8ff24e8 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -48,6 +48,9 @@ const struct cmd_entry cmd_pipe_pane_entry = { enum cmd_retval cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE_SLAVE + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct client *c; struct window_pane *wp; @@ -128,6 +131,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq) setblocking(wp->pipe_fd, 0); return (CMD_RETURN_NORMAL); } +#endif } void diff --git a/cmd-respawn-pane.c b/cmd-respawn-pane.c index 4486c91f..d5491b5c 100644 --- a/cmd-respawn-pane.c +++ b/cmd-respawn-pane.c @@ -43,6 +43,9 @@ const struct cmd_entry cmd_respawn_pane_entry = { enum cmd_retval cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE_SLAVE + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct winlink *wl; struct window *w; @@ -89,4 +92,5 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq) environ_free(&env); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-respawn-window.c b/cmd-respawn-window.c index 35bd3471..efca4b72 100644 --- a/cmd-respawn-window.c +++ b/cmd-respawn-window.c @@ -42,6 +42,9 @@ const struct cmd_entry cmd_respawn_window_entry = { enum cmd_retval cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE_SLAVE + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct winlink *wl; struct window *w; @@ -98,4 +101,5 @@ cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq) environ_free(&env); return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-send-keys.c b/cmd-send-keys.c index 37d4fd2b..d6445cee 100644 --- a/cmd-send-keys.c +++ b/cmd-send-keys.c @@ -52,6 +52,9 @@ const struct cmd_entry cmd_send_prefix_entry = { enum cmd_retval cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE_SLAVE + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct window_pane *wp; struct session *s; @@ -100,4 +103,5 @@ cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq) } return (CMD_RETURN_NORMAL); +#endif } diff --git a/cmd-server-info.c b/cmd-server-info.c index 8eba172a..ed47df35 100644 --- a/cmd-server-info.c +++ b/cmd-server-info.c @@ -123,8 +123,8 @@ cmd_server_info_exec(unused struct cmd *self, struct cmd_q *cmdq) sizeof *gl->celldata; } cmdq_print(cmdq, - "%6u: %s %lu %d %u/%u, %zu bytes", j, - wp->tty, (u_long) wp->pid, wp->fd, lines, + "%6u: %s %lu %u/%u, %zu bytes", j, + wp->tty, (u_long) wp->pid, lines, gd->hsize + gd->sy, size); j++; } diff --git a/cmd-set-option.c b/cmd-set-option.c index a46460a8..a610be4c 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -161,6 +161,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) } /* Start or stop timers when automatic-rename changed. */ +#ifndef TMATE_SLAVE if (strcmp (oe->name, "automatic-rename") == 0) { for (i = 0; i < ARRAY_LENGTH(&windows); i++) { if ((w = ARRAY_ITEM(&windows, i)) == NULL) @@ -171,6 +172,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq) evtimer_del(&w->name_timer); } } +#endif /* Update sizes and redraw. May not need it but meh. */ recalculate_sizes(); diff --git a/cmd-split-window.c b/cmd-split-window.c index 601dcb17..b2529ff3 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -52,6 +52,9 @@ cmd_split_window_key_binding(struct cmd *self, int key) enum cmd_retval cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE_SLAVE + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct session *s; struct winlink *wl; @@ -113,9 +116,7 @@ cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq) } hlimit = options_get_number(&s->options, "history-limit"); - shell = options_get_string(&s->options, "default-shell"); - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; + shell = _PATH_BSHELL; if ((lc = layout_split_pane(wp, type, size, 0)) == NULL) { cause = xstrdup("pane too small"); @@ -165,4 +166,5 @@ error: cmdq_error(cmdq, "create pane failed: %s", cause); free(cause); return (CMD_RETURN_ERROR); +#endif } diff --git a/cmd.c b/cmd.c index eecac462..a4160082 100644 --- a/cmd.c +++ b/cmd.c @@ -1290,6 +1290,9 @@ cmd_template_replace(const char *template, const char *s, int idx) const char * cmd_get_default_path(struct cmd_q *cmdq, const char *cwd) { +#ifdef TMATE_SLAVE + return NULL; +#else struct client *c = cmdq->client; struct session *s; struct environ_entry *envent; @@ -1361,4 +1364,5 @@ complete_path: if (n > 0 && (size_t)n < sizeof path) return (path); return (s->cwd); +#endif } diff --git a/configure.ac b/configure.ac index c0ef34c6..bfffefa1 100644 --- a/configure.ac +++ b/configure.ac @@ -107,6 +107,9 @@ AC_MSG_RESULT($found_glibc) # Look for clock_gettime. Must come before event_init. AC_SEARCH_LIBS(clock_gettime, rt) +AC_SEARCH_LIBS(ssh_new, ssh) +AC_SEARCH_LIBS(msgpack_object_print, msgpack) + # Look for libevent. PKG_CHECK_MODULES( LIBEVENT, diff --git a/create_keys.sh b/create_keys.sh new file mode 100755 index 00000000..b8a1d42b --- /dev/null +++ b/create_keys.sh @@ -0,0 +1,13 @@ +#!/bin/bash +gen_key() { + keytype=$1 + ks="${keytype}_" + key="keys/ssh_host_${ks}key" + if [ ! -e "${key}" ] ; then + ssh-keygen -t ${keytype} -f "${key}" -N '' + return $? + fi +} + +mkdir -p keys +gen_key dsa && gen_key rsa && gen_key ecdsa || exit 1 diff --git a/format.c b/format.c index fa2dd0b2..c635f00f 100644 --- a/format.c +++ b/format.c @@ -421,7 +421,9 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_title", "%s", wp->base.title); format_add(ft, "pane_id", "%%%u", wp->id); format_add(ft, "pane_active", "%d", wp == wp->window->active); +#ifndef TMATE_SLAVE format_add(ft, "pane_dead", "%d", wp->fd == -1); +#endif format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base); @@ -432,12 +434,14 @@ format_window_pane(struct format_tree *ft, struct window_pane *wp) format_add(ft, "pane_start_command", "%s", wp->cmd); if (wp->cwd != NULL) format_add(ft, "pane_start_path", "%s", wp->cwd); +#ifndef TMATE_SLAVE if ((cwd = osdep_get_cwd(wp->fd)) != NULL) format_add(ft, "pane_current_path", "%s", cwd); if ((cmd = osdep_get_name(wp->fd, wp->tty)) != NULL) { format_add(ft, "pane_current_command", "%s", cmd); free(cmd); } +#endif format_add(ft, "cursor_x", "%d", wp->base.cx); format_add(ft, "cursor_y", "%d", wp->base.cy); diff --git a/input-keys.c b/input-keys.c index faa7bd17..1c91a84b 100644 --- a/input-keys.c +++ b/input-keys.c @@ -23,6 +23,8 @@ #include "tmux.h" +#ifndef TMATE_SLAVE + /* * This file is rather misleadingly named, it contains the code which takes a * key code and translates it into something suitable to be sent to the @@ -253,3 +255,5 @@ input_mouse(struct window_pane *wp, struct session *s, struct mouse_event *m) } } } + +#endif diff --git a/input.c b/input.c index 4aa02e90..4cefa926 100644 --- a/input.c +++ b/input.c @@ -732,7 +732,11 @@ input_parse(struct window_pane *wp) { struct input_ctx *ictx = &wp->ictx; const struct input_transition *itr; +#ifdef TMATE_SLAVE + struct evbuffer *evb = wp->event_input; +#else struct evbuffer *evb = wp->event->input; +#endif u_char *buf; size_t len, off; @@ -760,7 +764,7 @@ input_parse(struct window_pane *wp) /* Parse the input. */ while (off < len) { ictx->ch = buf[off++]; - log_debug("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); + log_debug2("%s: '%c' %s", __func__, ictx->ch, ictx->state->name); /* Find the transition. */ itr = ictx->state->transitions; @@ -848,6 +852,7 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval) void input_reply(struct input_ctx *ictx, const char *fmt, ...) { +#ifndef TMATE_SLAVE va_list ap; char *reply; @@ -857,6 +862,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...) bufferevent_write(ictx->wp->event, reply, strlen(reply)); free(reply); +#endif } /* Clear saved state. */ @@ -936,7 +942,7 @@ input_c0_dispatch(struct input_ctx *ictx) struct screen *s = sctx->s; u_int trigger; - log_debug("%s: '%c", __func__, ictx->ch); + log_debug2("%s: '%c", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ @@ -974,7 +980,7 @@ input_c0_dispatch(struct input_ctx *ictx) ictx->cell.attr &= ~GRID_ATTR_CHARSET; break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } @@ -1000,12 +1006,12 @@ input_esc_dispatch(struct input_ctx *ictx) if (ictx->flags & INPUT_DISCARD) return (0); - log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf); + log_debug2("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf); entry = bsearch(ictx, input_esc_table, nitems(input_esc_table), sizeof input_esc_table[0], input_table_compare); if (entry == NULL) { - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); return (0); } @@ -1080,13 +1086,13 @@ input_csi_dispatch(struct input_ctx *ictx) return (0); if (input_split(ictx) != 0) return (0); - log_debug("%s: '%c' \"%s\" \"%s\"", + log_debug2("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), sizeof input_csi_table[0], input_table_compare); if (entry == NULL) { - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); return (0); } @@ -1131,7 +1137,7 @@ input_csi_dispatch(struct input_ctx *ictx) input_reply(ictx, "\033[?1;2c"); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1141,7 +1147,7 @@ input_csi_dispatch(struct input_ctx *ictx) input_reply(ictx, "\033[>0;95;0c"); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1168,7 +1174,7 @@ input_csi_dispatch(struct input_ctx *ictx) input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1195,7 +1201,7 @@ input_csi_dispatch(struct input_ctx *ictx) } break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1211,7 +1217,7 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_clearline(sctx); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1235,7 +1241,7 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1280,7 +1286,7 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1298,7 +1304,7 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1352,7 +1358,7 @@ input_csi_dispatch(struct input_ctx *ictx) screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1366,7 +1372,7 @@ input_csi_dispatch(struct input_ctx *ictx) bit_nclear(s->tabs, 0, screen_size_x(s) - 1); break; default: - log_debug("%s: unknown '%c'", __func__, ictx->ch); + log_debug2("%s: unknown '%c'", __func__, ictx->ch); break; } break; @@ -1539,7 +1545,7 @@ input_dcs_dispatch(struct input_ctx *ictx) if (ictx->flags & INPUT_DISCARD) return (0); - log_debug("%s: \"%s\"", __func__, ictx->input_buf); + log_debug2("%s: \"%s\"", __func__, ictx->input_buf); /* Check for tmux prefix. */ if (ictx->input_len >= prefix_len && @@ -1555,7 +1561,7 @@ input_dcs_dispatch(struct input_ctx *ictx) void input_enter_osc(struct input_ctx *ictx) { - log_debug("%s", __func__); + log_debug2("%s", __func__); input_clear(ictx); } @@ -1572,7 +1578,7 @@ input_exit_osc(struct input_ctx *ictx) if (ictx->input_len < 1 || *p < '0' || *p > '9') return; - log_debug("%s: \"%s\"", __func__, p); + log_debug2("%s: \"%s\"", __func__, p); option = 0; while (*p >= '0' && *p <= '9') @@ -1595,7 +1601,7 @@ input_exit_osc(struct input_ctx *ictx) screen_set_cursor_colour(ictx->ctx.s, ""); break; default: - log_debug("%s: unknown '%u'", __func__, option); + log_debug2("%s: unknown '%u'", __func__, option); break; } } @@ -1604,7 +1610,7 @@ input_exit_osc(struct input_ctx *ictx) void input_enter_apc(struct input_ctx *ictx) { - log_debug("%s", __func__); + log_debug2("%s", __func__); input_clear(ictx); } @@ -1615,7 +1621,7 @@ input_exit_apc(struct input_ctx *ictx) { if (ictx->flags & INPUT_DISCARD) return; - log_debug("%s: \"%s\"", __func__, ictx->input_buf); + log_debug2("%s: \"%s\"", __func__, ictx->input_buf); screen_set_title(ictx->ctx.s, ictx->input_buf); server_status_window(ictx->wp->window); @@ -1625,7 +1631,7 @@ input_exit_apc(struct input_ctx *ictx) void input_enter_rename(struct input_ctx *ictx) { - log_debug("%s", __func__); + log_debug2("%s", __func__); input_clear(ictx); } @@ -1638,7 +1644,7 @@ input_exit_rename(struct input_ctx *ictx) return; if (!options_get_number(&ictx->wp->window->options, "allow-rename")) return; - log_debug("%s: \"%s\"", __func__, ictx->input_buf); + log_debug2("%s: \"%s\"", __func__, ictx->input_buf); window_set_name(ictx->wp->window, ictx->input_buf); options_set_number(&ictx->wp->window->options, "automatic-rename", 0); @@ -1655,7 +1661,7 @@ input_utf8_open(struct input_ctx *ictx) input_print(ictx); return (-1); } - log_debug("%s", __func__); + log_debug2("%s", __func__); utf8_open(&ictx->utf8data, ictx->ch); return (0); @@ -1665,7 +1671,7 @@ input_utf8_open(struct input_ctx *ictx) int input_utf8_add(struct input_ctx *ictx) { - log_debug("%s", __func__); + log_debug2("%s", __func__); utf8_append(&ictx->utf8data, ictx->ch); return (0); @@ -1675,7 +1681,7 @@ input_utf8_add(struct input_ctx *ictx) int input_utf8_close(struct input_ctx *ictx) { - log_debug("%s", __func__); + log_debug2("%s", __func__); utf8_append(&ictx->utf8data, ictx->ch); diff --git a/log.c b/log.c index dbf9ee15..9b0e79ba 100644 --- a/log.c +++ b/log.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "tmux.h" @@ -48,9 +49,14 @@ log_event_cb(unused int severity, const char *msg) void log_open(int level, const char *path) { - log_file = fopen(path, "w"); - if (log_file == NULL) - return; + if (path) { + log_file = fopen(path, "a"); + if (log_file == NULL) + return; + } else { + log_file = stderr; + } + log_level = level; setlinebuf(log_file); @@ -78,7 +84,7 @@ log_vwrite(const char *msg, va_list ap) if (log_file == NULL) return; - if (asprintf(&fmt, "%s\n", msg) == -1) + if (asprintf(&fmt, "[%d] %s\n", getpid(), msg) == -1) exit(1); if (vfprintf(log_file, fmt, ap) == -1) exit(1); diff --git a/names.c b/names.c index f536d2fc..719c49e3 100644 --- a/names.c +++ b/names.c @@ -26,6 +26,22 @@ #include "tmux.h" +#ifdef TMATE_SLAVE + +char * +default_window_name(struct window *w) +{ + return xstrdup("[tmux]"); +} + +void +queue_window_name(struct window *w) +{ +} + +#else + + void window_name_callback(unused int, unused short, void *); char *parse_window_name(const char *); @@ -128,3 +144,5 @@ parse_window_name(const char *in) free(copy); return (name); } + +#endif diff --git a/paste.c b/paste.c index 7cbbbfb3..6de4eeaa 100644 --- a/paste.c +++ b/paste.c @@ -167,6 +167,7 @@ paste_print(struct paste_buffer *pb, size_t width) return (buf); } +#ifndef TMATE_SLAVE /* Paste into a window pane, filtering '\n' according to separator. */ void paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, @@ -192,3 +193,4 @@ paste_send_pane (struct paste_buffer *pb, struct window_pane *wp, if (bracket) bufferevent_write(wp->event, "\033[201~", 6); } +#endif diff --git a/resize.c b/resize.c index 5c365dfe..f0e9dfe0 100644 --- a/resize.c +++ b/resize.c @@ -21,6 +21,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Recalculate window and session sizes. @@ -71,6 +72,12 @@ recalculate_sizes(void) ssy = c->tty.sy; } } + +#ifdef TMATE_SLAVE + tmate_client_resize(ssx, ssy); + return; +#endif + if (ssx == UINT_MAX || ssy == UINT_MAX) { s->flags |= SESSION_UNATTACHED; continue; diff --git a/screen-redraw.c b/screen-redraw.c index a7bf81ff..cacc9da9 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -21,6 +21,7 @@ #include #include "tmux.h" +#include "tmate.h" int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); int screen_redraw_cell_border(struct client *, u_int, u_int); diff --git a/server-client.c b/server-client.c index 77e6de78..edcc8b64 100644 --- a/server-client.c +++ b/server-client.c @@ -27,6 +27,7 @@ #include #include "tmux.h" +#include "tmate.h" void server_client_check_focus(struct window_pane *); void server_client_check_resize(struct window_pane *); @@ -281,6 +282,7 @@ server_client_status_timer(void) } } +#ifndef TMATE_SLAVE /* Check for mouse keys. */ void server_client_check_mouse(struct client *c, struct window_pane *wp) @@ -333,6 +335,7 @@ server_client_check_mouse(struct client *c, struct window_pane *wp) /* Update last and pass through to client. */ window_pane_mouse(wp, c->session, m); } +#endif /* Is this fast enough to probably be a paste? */ int @@ -354,6 +357,9 @@ server_client_assume_paste(struct session *s) void server_client_handle_key(struct client *c, int key) { +#ifdef TMATE_SLAVE + tmate_client_key(key); +#else struct session *s; struct window *w; struct window_pane *wp; @@ -479,6 +485,7 @@ server_client_handle_key(struct client *c, int key) /* Dispatch the command. */ key_bindings_dispatch(bd, c); +#endif } /* Client functions that need to happen every loop. */ @@ -513,13 +520,16 @@ server_client_loop(void) w->flags &= ~WINDOW_REDRAW; TAILQ_FOREACH(wp, &w->panes, entry) { +#ifndef TMATE_SLAVE server_client_check_focus(wp); server_client_check_resize(wp); +#endif wp->flags &= ~PANE_REDRAW; } } } +#ifndef TMATE_SLAVE /* Check if pane should be resized. */ void server_client_check_resize(struct window_pane *wp) @@ -597,6 +607,7 @@ focused: bufferevent_write(wp->event, "\033[I", 3); wp->flags |= PANE_FOCUSED; } +#endif /* * Update cursor position and mode settings. The scroll region and attributes @@ -811,7 +822,7 @@ server_client_msg_dispatch(struct client *c) continue; } - log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); + log_debug2("got %d from client %d", imsg.hdr.type, c->ibuf.fd); switch (imsg.hdr.type) { case MSG_COMMAND: if (datalen != sizeof commanddata) @@ -996,10 +1007,7 @@ server_client_msg_shell(struct client *c) struct msg_shell_data data; const char *shell; - shell = options_get_string(&global_s_options, "default-shell"); - - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; + shell = _PATH_BSHELL; if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); diff --git a/server-fn.c b/server-fn.c index 566925f0..6c51989d 100644 --- a/server-fn.c +++ b/server-fn.c @@ -64,7 +64,7 @@ server_write_client( if (c->flags & CLIENT_BAD) return (-1); - log_debug("writing %d to client %d", type, c->ibuf.fd); + log_debug2("writing %d to client %d", type, c->ibuf.fd); error = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, -1, (void *) buf, len); if (error == 1) @@ -351,6 +351,7 @@ void server_destroy_pane(struct window_pane *wp) { struct window *w = wp->window; +#ifndef TMATE_SLAVE int old_fd; struct screen_write_ctx ctx; struct grid_cell gc; @@ -376,6 +377,7 @@ server_destroy_pane(struct window_pane *wp) wp->flags |= PANE_REDRAW; return; } +#endif server_unzoom_window(w); layout_close_pane(wp); diff --git a/server.c b/server.c index 4bfa9185..6781249d 100644 --- a/server.c +++ b/server.c @@ -36,6 +36,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Main server functions. @@ -105,10 +106,13 @@ server_create_socket(void) int server_start(int lockfd, char *lockfile) { +#ifndef TMATE_SLAVE int pair[2]; +#endif struct timeval tv; char *cause; +#ifndef TMATE_SLAVE /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) fatal("socketpair failed"); @@ -135,8 +139,9 @@ server_start(int lockfd, char *lockfile) if (event_reinit(ev_base) != 0) fatal("event_reinit failed"); clear_signals(0); - logfile("server"); +#endif + log_debug("server started, pid %ld", (long) getpid()); ARRAY_INIT(&windows); @@ -158,11 +163,14 @@ server_start(int lockfd, char *lockfile) #endif server_fd = server_create_socket(); + +#ifndef TMATE_SLAVE server_client_create(pair[1]); unlink(lockfile); free(lockfile); close(lockfd); +#endif cfg_cmd_q = cmdq_new(NULL); cfg_cmd_q->emptyfn = cfg_default_done; @@ -203,7 +211,8 @@ server_start(int lockfd, char *lockfile) void server_loop(void) { - while (!server_should_shutdown()) { + while (!server_shutdown) { + //while (!server_should_shutdown()) { event_loop(EVLOOP_ONCE); server_window_loop(); diff --git a/session.c b/session.c index 74eb06a5..097ab6e5 100644 --- a/session.c +++ b/session.c @@ -25,6 +25,7 @@ #include #include "tmux.h" +#include "tmate.h" /* Global session list. */ struct sessions sessions; @@ -244,9 +245,7 @@ session_new(struct session *s, environ_copy(&s->environ, &env); server_fill_environ(s, &env); - shell = options_get_string(&s->options, "default-shell"); - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; + shell = _PATH_BSHELL; hlimit = options_get_number(&s->options, "history-limit"); w = window_create( diff --git a/tmate-debug.c b/tmate-debug.c new file mode 100644 index 00000000..71311556 --- /dev/null +++ b/tmate-debug.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include "tmate.h" + +#if DEBUG + +static int print_resolved_stack_frame(const char *frame) +{ + char file[100]; + char cmd[200]; + char output[300]; + char address[20]; + char *line; + FILE *ps; + + static regex_t _regex; + static regex_t *regex; + regmatch_t matches[3]; + + if (!regex) { + if (regcomp(&_regex, "(.+)\\(\\) \\[([^]]+)\\]", REG_EXTENDED)) + return -1; + regex = &_regex; + } + + if (regexec(regex, frame, 3, matches, 0)) + return -1; + + memcpy(file, &frame[matches[1].rm_so], matches[1].rm_eo - matches[1].rm_so); + file[matches[1].rm_eo - matches[1].rm_so] = 0; + + memcpy(address, &frame[matches[2].rm_so], matches[2].rm_eo - matches[2].rm_so); + address[matches[2].rm_eo - matches[2].rm_so] = 0; + + sprintf(cmd, "addr2line -e %s %s -f -p -s", file, address); + + ps = popen(cmd, "r"); + if (!ps) + return -1; + + line = fgets(output, sizeof(output), ps); + pclose(ps); + + if (!line) + return -1; + + line[strlen(line)-1] = 0; /* remove \n */ + tmate_debug("%s(%s) [%s]", file, line, address); + return 0; +} +#endif + +void +tmate_print_trace (void) +{ + void *array[20]; + size_t size; + char **strings; + size_t i; + + size = backtrace (array, 20); + strings = backtrace_symbols (array, size); + + tmate_debug ("============ %zd stack frames ============", size); + + for (i = 1; i < size; i++) { +#if DEBUG + if (print_resolved_stack_frame(strings[i]) < 0) +#endif + tmate_debug("%s", strings[i]); + } + + free (strings); +} diff --git a/tmate-decoder.c b/tmate-decoder.c new file mode 100644 index 00000000..570f5f9c --- /dev/null +++ b/tmate-decoder.c @@ -0,0 +1,260 @@ +#include "tmate.h" + +static struct session *main_session; + +struct tmate_unpacker { + msgpack_object *argv; + int argc; +}; + +static void decoder_error(void) +{ +#ifdef DEBUG + tmate_print_trace(); +#endif + tmate_fatal("Received a bad message"); +} + +static void init_unpacker(struct tmate_unpacker *uk, + msgpack_object obj) +{ + if (obj.type != MSGPACK_OBJECT_ARRAY) + decoder_error(); + + uk->argv = obj.via.array.ptr; + uk->argc = obj.via.array.size; +} + +static int64_t unpack_int(struct tmate_unpacker *uk) +{ + int64_t val; + + if (uk->argc == 0) + decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER && + uk->argv[0].type != MSGPACK_OBJECT_NEGATIVE_INTEGER) + decoder_error(); + + val = uk->argv[0].via.i64; + + uk->argv++; + uk->argc--; + + return val; +} + +static void unpack_raw(struct tmate_unpacker *uk, + const char **buf, size_t *len) +{ + if (uk->argc == 0) + decoder_error(); + + if (uk->argv[0].type != MSGPACK_OBJECT_RAW) + decoder_error(); + + *len = uk->argv[0].via.raw.size; + *buf = uk->argv[0].via.raw.ptr; + + uk->argv++; + uk->argc--; +} + +static char *unpack_string(struct tmate_unpacker *uk) +{ + const char *buf; + char *alloc_buf; + size_t len; + + unpack_raw(uk, &buf, &len); + + alloc_buf = xmalloc(len + 1); + memcpy(alloc_buf, buf, len); + alloc_buf[len] = '\0'; + + return alloc_buf; +} + +static void unpack_array(struct tmate_unpacker *uk, + struct tmate_unpacker *nested) +{ + if (uk->argc == 0) + decoder_error(); + + init_unpacker(nested, uk->argv[0]); + + uk->argv++; + uk->argc--; +} + +#define unpack_each(nested_uk, tmp_uk, uk) \ + for (unpack_array(uk, tmp_uk); \ + (tmp_uk)->argc > 0 && (init_unpacker(nested_uk, (tmp_uk)->argv[0]), 1); \ + (tmp_uk)->argv++, (tmp_uk)->argc--) + +static void tmate_header(struct tmate_unpacker *uk) +{ + int protocol = unpack_int(uk); + + if (protocol != 1) + decoder_error(); + + tmate_debug("new master, protocol: %d", protocol); +} + +extern u_int next_window_pane_id; + +static void tmate_sync_window_panes(struct window *w, + struct tmate_unpacker *w_uk) +{ + struct tmate_unpacker uk, tmp_uk; + struct window_pane *wp, *wp_tmp; + int active_pane_id; + + TAILQ_FOREACH(wp, &w->panes, entry) + wp->flags |= PANE_KILL; + + unpack_each(&uk, &tmp_uk, w_uk) { + int id = unpack_int(&uk); + int sx = unpack_int(&uk); + int sy = unpack_int(&uk); + int xoff = unpack_int(&uk); + int yoff = unpack_int(&uk); + + wp = window_pane_find_by_id(id); + if (!wp) { + next_window_pane_id = id; + wp = window_add_pane(w, TMATE_HLIMIT); + window_set_active_pane(w, wp); + } + wp->flags &= ~PANE_KILL; + + wp->xoff = xoff; + wp->yoff = yoff; + window_pane_resize(wp, sx, sy); + + wp->flags |= PANE_REDRAW; + } + + TAILQ_FOREACH_SAFE(wp, &w->panes, entry, wp_tmp) { + if (wp->flags & PANE_KILL) + window_remove_pane(w, wp); + } + + active_pane_id = unpack_int(w_uk); + wp = window_pane_find_by_id(active_pane_id); + if (wp && wp->window == w) + window_set_active_pane(w, wp); +} + +static void tmate_sync_window(struct tmate_unpacker *uk) +{ + struct session *s; + struct winlink *wl; + struct window *w; + char *cause; + + int id = unpack_int(uk); + char *name = unpack_string(uk); + int sx = unpack_int(uk); + int sy = unpack_int(uk); + + if (!main_session) { + main_session = session_create("default", NULL, "default", NULL, + NULL, 0, sx, sy, &cause); + if (!main_session) + tmate_fatal("can't create main session"); + } + s = main_session; + + wl = winlink_find_by_index(&s->windows, id); + if (!wl) { + wl = session_new(s, name, "sh", NULL, id, &cause); + if (!wl) + tmate_fatal("can't create window id=%d", id); + session_select(s, RB_ROOT(&s->windows)->idx); + } + w = wl->window; + + free(w->name); + w->name = name; + s->sx = w->sx = sx; + s->sy = w->sy = sy; + + tmate_sync_window_panes(w, uk); + + server_redraw_window(w); +} + +static void tmate_pty_data(struct tmate_unpacker *uk) +{ + struct window_pane *wp; + const char *buf; + size_t len; + int id; + + id = unpack_int(uk); + unpack_raw(uk, &buf, &len); + + wp = window_pane_find_by_id(id); + if (!wp) + tmate_fatal("can't find pane id=%d", id); + + evbuffer_add(wp->event_input, buf, len); + input_parse(wp); + + wp->window->flags |= WINDOW_SILENCE; +} + +static void handle_message(msgpack_object obj) +{ + struct tmate_unpacker _uk; + struct tmate_unpacker *uk = &_uk; + int cmd; + + init_unpacker(uk, obj); + + switch (unpack_int(uk)) { + case TMATE_HEADER: tmate_header(uk); break; + case TMATE_SYNC_WINDOW: tmate_sync_window(uk); break; + case TMATE_PTY_DATA: tmate_pty_data(uk); break; + default: decoder_error(); + } +} + +void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len) +{ + msgpack_unpacked result; + + msgpack_unpacker_buffer_consumed(&decoder->unpacker, len); + + msgpack_unpacked_init(&result); + while (msgpack_unpacker_next(&decoder->unpacker, &result)) { + handle_message(result.data); + } + msgpack_unpacked_destroy(&result); + + if (msgpack_unpacker_message_size(&decoder->unpacker) > + TMATE_MAX_MESSAGE_SIZE) { + tmate_fatal("Message too big"); + } +} + +void tmate_decoder_get_buffer(struct tmate_decoder *decoder, + char **buf, size_t *len) +{ + /* rewind the buffer if possible */ + if (msgpack_unpacker_buffer_capacity(&decoder->unpacker) < + TMATE_MAX_MESSAGE_SIZE) { + msgpack_unpacker_expand_buffer(&decoder->unpacker, 0); + } + + *buf = msgpack_unpacker_buffer(&decoder->unpacker); + *len = msgpack_unpacker_buffer_capacity(&decoder->unpacker); +} + +void tmate_decoder_init(struct tmate_decoder *decoder) +{ + if (!msgpack_unpacker_init(&decoder->unpacker, 2*TMATE_MAX_MESSAGE_SIZE)) + tmate_fatal("cannot initialize the unpacker"); +} diff --git a/tmate-encoder.c b/tmate-encoder.c new file mode 100644 index 00000000..810eb8af --- /dev/null +++ b/tmate-encoder.c @@ -0,0 +1,39 @@ +#include "tmate.h" + +static int msgpack_write(void *data, const char *buf, unsigned int len) +{ + struct tmate_encoder *encoder = data; + + evbuffer_add(encoder->buffer, buf, len); + + if ((encoder->ev_readable.ev_flags & EVLIST_INSERTED) && + !(encoder->ev_readable.ev_flags & EVLIST_ACTIVE)) { + event_active(&encoder->ev_readable, EV_READ, 0); + } + + return 0; +} + +void tmate_encoder_init(struct tmate_encoder *encoder) +{ + encoder->buffer = evbuffer_new(); + msgpack_packer_init(&encoder->pk, encoder, &msgpack_write); +} + +#define pack(what, ...) msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__) + +void tmate_client_key(int key) +{ + pack(array, 2); + pack(int, TMATE_CLIENT_KEY); + pack(int, key); +} + +void tmate_client_resize(u_int sx, u_int sy) +{ + pack(array, 3); + pack(int, TMATE_CLIENT_RESIZE); + /* cast to signed, -1 == no clients */ + pack(int, sx); + pack(int, sy); +} diff --git a/tmate-server.c b/tmate-server.c new file mode 100644 index 00000000..6196629a --- /dev/null +++ b/tmate-server.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include "tmate.h" + +struct event_base *ev_base; + +struct options global_options; /* server options */ +struct options global_s_options; /* session options */ +struct options global_w_options; /* window options */ +struct environ global_environ; + +struct event_base *ev_base; + +char *cfg_file; +char *shell_cmd; +int debug_level; +time_t start_time; +char socket_path[MAXPATHLEN]; +int login_shell; +char *environ_path; +pid_t environ_pid = -1; +int environ_session_id = -1; + +void +setblocking(int fd, int state) +{ + int mode; + + if ((mode = fcntl(fd, F_GETFL)) != -1) { + if (!state) + mode |= O_NONBLOCK; + else + mode &= ~O_NONBLOCK; + fcntl(fd, F_SETFL, mode); + } +} + +const char* +get_full_path(const char *wd, const char *path) +{ + static char newpath[MAXPATHLEN]; + char oldpath[MAXPATHLEN]; + + if (getcwd(oldpath, sizeof oldpath) == NULL) + return (NULL); + if (chdir(wd) != 0) + return (NULL); + if (realpath(path, newpath) != 0) + return (NULL); + chdir(oldpath); + return (newpath); +} + +static void usage(void) +{ + fprintf(stderr, "usage: tmate-server [-p PORT]\n"); +} + +int main(int argc, char **argv) +{ + int opt; + int port = 22; + char *log_path = NULL; /* stderr */ + + strcpy(socket_path, "/tmp/tmate-slave"); + + while ((opt = getopt(argc, argv, "p:l:v")) != -1) { + switch (opt) { + case 'p': + port = atoi(optarg); + break; + case 'l': + log_path = optarg; + break; + case 'v': + debug_level++; + break; + default: + usage(); + return 1; + } + } + + log_open(debug_level, log_path); + tmate_ssh_server_main(port); + return 0; +} + +struct tmate_encoder *tmate_encoder; + +void tmate_spawn_slave_server(struct tmate_ssh_client *client) +{ + int quiet = 0; + int flags = IDENTIFY_UTF8 | IDENTIFY_256COLOURS; + struct tmate_encoder encoder; + struct tmate_decoder decoder; + + tmate_debug("Spawn tmux slave server"); + + ev_base = osdep_event_init(); + + tmate_encoder_init(&encoder); + tmate_decoder_init(&decoder); + tmate_encoder = &encoder; + + tmate_ssh_client_init(client, &encoder, &decoder); + + environ_init(&global_environ); + + options_init(&global_options, NULL); + options_table_populate_tree(server_options_table, &global_options); + options_set_number(&global_options, "quiet", quiet); + + options_init(&global_s_options, NULL); + options_table_populate_tree(session_options_table, &global_s_options); + + options_init(&global_w_options, NULL); + options_table_populate_tree(window_options_table, &global_w_options); + + if (flags & IDENTIFY_UTF8) { + options_set_number(&global_s_options, "status-utf8", 1); + options_set_number(&global_s_options, "mouse-utf8", 1); + options_set_number(&global_w_options, "utf8", 1); + } + + server_start(0, NULL); + /* never reached */ +} + +void tmate_spawn_slave_client(struct tmate_ssh_client *ssh_client) +{ + struct tmate_ssh_client_pty _client; + struct tmate_ssh_client_pty *client = &_client; + int slave_pty; + int ret; + char *argv[] = {(char *)"attach", NULL}; + + client->session = ssh_client->session; + client->channel = ssh_client->channel; + client->winsize_pty = ssh_client->winsize_pty; + + tmate_debug("Spawn tmux slave client"); + + ev_base = osdep_event_init(); + + if (openpty(&client->pty, &slave_pty, NULL, NULL, NULL) < 0) + tmate_fatal("Cannot allocate pty"); + + /* setsid(); */ + /* ioctl(slave_pty, TIOCSCTTY, NULL); */ + + dup2(slave_pty, STDIN_FILENO); + dup2(slave_pty, STDOUT_FILENO); + stderr = stdout; + close(slave_pty); + + tmate_ssh_client_pty_init(client); + + ret = client_main(1, argv, IDENTIFY_UTF8 | IDENTIFY_256COLOURS); + tmate_flush_pty(client); + exit(ret); +} diff --git a/tmate-ssh-client-pty.c b/tmate-ssh-client-pty.c new file mode 100644 index 00000000..89a63c54 --- /dev/null +++ b/tmate-ssh-client-pty.c @@ -0,0 +1,143 @@ +#include "tmate.h" +#include +#include +#include +#include + +static void consume_channel(struct tmate_ssh_client_pty *client) +{ + ssize_t len, written; + char buf[4096]; + char *ptr; + + for (;;) { + len = ssh_channel_read_nonblocking(client->channel, + buf, sizeof(buf), 0); + if (len < 0) { + tmate_debug("Error reading from channel: %s", + ssh_get_error(client->session)); + exit(1); + } + + if (len == 0) + return; + + ptr = buf; + setblocking(client->pty, 1); + while (len > 0) { + written = write(client->pty, ptr, len); + if (written < 0) + tmate_fatal("Error writing to pty"); + + ptr += written; + len -= written; + } + setblocking(client->pty, 0); + } +} + +static void on_session_event(struct tmate_ssh_client_pty *client) +{ + ssh_execute_message_callbacks(client->session); + + consume_channel(client); + + if (!ssh_is_connected(client->session)) { + tmate_debug("Disconnected"); + exit(1); + } +} + +static void __on_session_event(evutil_socket_t fd, short what, void *arg) +{ + on_session_event(arg); +} + +static int message_callback(struct tmate_ssh_client_pty *client, + ssh_message msg) +{ + if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL && + ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_WINDOW_CHANGE) { + struct winsize ws; + + ws.ws_col = ssh_message_channel_request_pty_width(msg); + ws.ws_row = ssh_message_channel_request_pty_height(msg); + + tmate_debug("change window size to %d, %d", + ws.ws_col, ws.ws_row); + ioctl(client->pty, TIOCSWINSZ, &ws); + kill(getpid(), SIGWINCH); + + return 1; + } + return 0; +} + +static int __message_callback(ssh_session session, ssh_message msg, void *arg) +{ + return message_callback(arg, msg); +} + +static void register_session_fd_event(struct tmate_ssh_client_pty *client) +{ + ssh_set_message_callback(client->session, __message_callback, client); + + event_assign(&client->ev_ssh, ev_base, ssh_get_fd(client->session), + EV_READ | EV_PERSIST, __on_session_event, client); + event_add(&client->ev_ssh, NULL); +} + +static void on_pty_event(struct tmate_ssh_client_pty *client) +{ + ssize_t len, written; + char buf[4096]; + + for (;;) { + len = read(client->pty, buf, sizeof(buf)); + if (len < 0) { + if (errno == EAGAIN) + return; + tmate_fatal("pty read error"); + } + + if (len == 0) + tmate_fatal("pty reached EOF"); + + written = ssh_channel_write(client->channel, buf, len); + if (written < 0) { + tmate_debug("Error writing to channel: %s", + ssh_get_error(client->session)); + exit(1); + } + if (len != written) { + tmate_fatal("Cannot write %d bytes, wrote %d", + (int)len, (int)written); + } + } +} + +static void __on_pty_event(evutil_socket_t fd, short what, void *arg) +{ + on_pty_event(arg); +} + +void tmate_flush_pty(struct tmate_ssh_client_pty *client) +{ + on_pty_event(client); + close(client->pty); +} + +static void register_pty_event(struct tmate_ssh_client_pty *client) +{ + setblocking(client->pty, 0); + event_assign(&client->ev_pty, ev_base, client->pty, + EV_READ | EV_PERSIST, __on_pty_event, client); + event_add(&client->ev_pty, NULL); +} + +void tmate_ssh_client_pty_init(struct tmate_ssh_client_pty *client) +{ + ioctl(client->pty, TIOCSWINSZ, &client->winsize_pty); + register_session_fd_event(client); + register_pty_event(client); +} diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c new file mode 100644 index 00000000..6737b3d4 --- /dev/null +++ b/tmate-ssh-client.c @@ -0,0 +1,103 @@ +#include "tmate.h" +#include +#include +#include + +static void consume_channel(struct tmate_ssh_client *client) +{ + char *buf; + ssize_t len; + + for (;;) { + tmate_decoder_get_buffer(client->decoder, &buf, &len); + if (len == 0) + tmate_fatal("Input buffer full"); + + len = ssh_channel_read_nonblocking(client->channel, + buf, len, 0); + if (len < 0) { + tmate_debug("Error reading from channel: %s", + ssh_get_error(client->session)); + exit(1); + } + if (len == 0) + break; + + tmate_decoder_commit(client->decoder, len); + } +} + +static void on_session_event(struct tmate_ssh_client *client) +{ + ssh_session session = client->session; + ssh_channel channel = client->channel; + + consume_channel(client); + if (!ssh_is_connected(session)) { + tmate_debug("Disconnected"); + exit(1); + } +} + +static void __on_session_event(evutil_socket_t fd, short what, void *arg) +{ + on_session_event(arg); +} + +static void register_session_fd_event(struct tmate_ssh_client *client) +{ + event_assign(&client->ev_ssh, ev_base, ssh_get_fd(client->session), + EV_READ | EV_PERSIST, __on_session_event, client); + event_add(&client->ev_ssh, NULL); +} + +static void flush_input_stream(struct tmate_ssh_client *client) +{ + struct evbuffer *evb = client->encoder->buffer; + ssize_t len, written; + char *buf; + + for (;;) { + len = evbuffer_get_length(evb); + if (!len) + break; + + buf = evbuffer_pullup(evb, -1); + + written = ssh_channel_write(client->channel, buf, len); + if (written < 0) { + tmate_debug("Error writing to channel: %s", + ssh_get_error(client->session)); + exit(1); + } + + evbuffer_drain(evb, written); + } +} + +static void __flush_input_stream(evutil_socket_t fd, short what, void *arg) +{ + flush_input_stream(arg); +} + +static void register_input_stream_event(struct tmate_ssh_client *client) +{ + event_assign(&client->encoder->ev_readable, ev_base, -1, + EV_READ | EV_PERSIST, __flush_input_stream, client); + event_add(&client->encoder->ev_readable, NULL); +} + + +void tmate_ssh_client_init(struct tmate_ssh_client *client, + struct tmate_encoder *encoder, + struct tmate_decoder *decoder) +{ + client->winsize_pty.ws_col = 80; + client->winsize_pty.ws_row = 24; + + client->encoder = encoder; + client->decoder = decoder; + + register_session_fd_event(client); + register_input_stream_event(client); +} diff --git a/tmate-ssh-server.c b/tmate-ssh-server.c new file mode 100644 index 00000000..8fc4ced1 --- /dev/null +++ b/tmate-ssh-server.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tmate.h" + +#define SSH_GRACE_PERIOD 60 + +static void client_bootstrap(struct tmate_ssh_client *client) +{ + int auth = 0; + ssh_session session = client->session; + ssh_channel channel = NULL; + ssh_message msg; + + int flag = 1; + setsockopt(ssh_get_fd(session), IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); + alarm(SSH_GRACE_PERIOD); + + tmate_debug("Exchanging DH keys"); + + if (ssh_handle_key_exchange(session) < 0) + tmate_fatal("Error doing the key exchange"); + + tmate_debug("Authenticating with public key"); + + while (!auth) { + msg = ssh_message_get(session); + if (!msg) + tmate_fatal("Authentification error"); + + switch (ssh_message_type(msg)) { + case SSH_REQUEST_AUTH: + switch (ssh_message_subtype(msg)) { + case SSH_AUTH_METHOD_PUBLICKEY: + if (ssh_message_auth_publickey_state(msg) == SSH_PUBLICKEY_STATE_NONE) + ssh_message_auth_reply_pk_ok_simple(msg); + + else if (ssh_message_auth_publickey_state(msg) == SSH_PUBLICKEY_STATE_VALID) { + ssh_message_auth_reply_success(msg, 0); + auth = 1; + } + break; + case SSH_AUTH_METHOD_NONE: + default: + ssh_message_auth_set_methods(msg, SSH_AUTH_METHOD_PUBLICKEY); + ssh_message_reply_default(msg); + break; + } + break; + default: + ssh_message_reply_default(msg); + } + + ssh_message_free(msg); + } + + tmate_debug("Opening channel"); + + while (!channel) { + msg = ssh_message_get(session); + if (!msg) + tmate_fatal("Error getting channel"); + + if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL_OPEN && + ssh_message_subtype(msg) == SSH_CHANNEL_SESSION) { + client->channel = channel = ssh_message_channel_request_open_reply_accept(msg); + if (!channel) + tmate_fatal("Error getting channel"); + } else { + ssh_message_reply_default(msg); + } + + ssh_message_free(msg); + } + + tmate_debug("Getting client type"); + + while (1) { + msg = ssh_message_get(session); + if (!msg) + tmate_fatal("Error getting subsystem"); + + /* subsystem request */ + if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL && + ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_SUBSYSTEM && + !strcmp(ssh_message_channel_request_subsystem(msg), "tmate")) { + alarm(0); + ssh_message_channel_request_reply_success(msg); + tmate_spawn_slave_server(client); + } + + /* PTY request */ + else if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL && + ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_PTY) { + + client->winsize_pty.ws_col = ssh_message_channel_request_pty_width(msg); + client->winsize_pty.ws_row = ssh_message_channel_request_pty_height(msg); + ssh_message_channel_request_reply_success(msg); + } + + /* SHELL request */ + else if (ssh_message_type(msg) == SSH_REQUEST_CHANNEL && + ssh_message_subtype(msg) == SSH_CHANNEL_REQUEST_SHELL) { + alarm(0); + ssh_message_channel_request_reply_success(msg); + tmate_spawn_slave_client(client); + } + + /* Default */ + else { + ssh_message_reply_default(msg); + } + + ssh_message_free(msg); + } +} + +static void handle_sigchld(void) +{ + int status, child_dead, child_exit_status; + pid_t pid; + + while ((pid = waitpid(0, &status, WNOHANG)) > 0) { + child_dead = 0; + + if (WIFEXITED(status)) { + child_dead = 1; + child_exit_status = WEXITSTATUS(status); + } + + if (WIFSIGNALED(status)) { + child_dead = 1; child_exit_status = EXIT_FAILURE; + } + + if (!child_dead) + continue; + + tmate_debug("Child reaped pid=%d exit=%d", pid, child_exit_status); + } while (pid > 0); +} + +static void handle_sigalrm(void) +{ + log_fatal("Connection grace period (%d) passed", SSH_GRACE_PERIOD); +} + +static void signal_handler(int sig) +{ + switch (sig) { + case SIGCHLD: handle_sigchld(); break; + case SIGALRM: handle_sigalrm(); break; + } +} + +static void setup_signals(void) +{ + signal(SIGCHLD, signal_handler); + signal(SIGALRM, signal_handler); +} + +static void ssh_log_cb(ssh_session session, int priority, + const char *message, void *userdata) +{ + tmate_debug("[%d] %s", priority, message); +} + +static struct ssh_callbacks_struct ssh_session_callbacks = { + .log_function = ssh_log_cb +}; + +void tmate_ssh_server_main(int port) +{ + struct tmate_ssh_client _client; + struct tmate_ssh_client *client = &_client; + ssh_bind bind; + pid_t pid; + + int verbosity = SSH_LOG_NOLOG; + //int verbosity = SSH_LOG_PACKET; + + setup_signals(); + ssh_callbacks_init(&ssh_session_callbacks); + + bind = ssh_bind_new(); + if (!bind) + log_fatal("Cannot initialize ssh"); + + ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT, &port); + ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BANNER, SSH_BANNER); + ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &verbosity); + ssh_bind_options_set(bind, SSH_BIND_OPTIONS_DSAKEY, "keys/ssh_host_dsa_key"); + ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSAKEY, "keys/ssh_host_rsa_key"); + + if (ssh_bind_listen(bind) < 0) + log_fatal("Error listening to socket: %s\n", ssh_get_error(bind)); + + for (;;) { + client->session = ssh_new(); + client->channel = NULL; + if (!client->session) + tmate_fatal("Cannot initialize session"); + + ssh_set_callbacks(client->session, &ssh_session_callbacks); + + if (ssh_bind_accept(bind, client->session) < 0) + tmate_fatal("Error accepting connection: %s", ssh_get_error(bind)); + + if ((pid = fork()) < 0) + tmate_fatal("Can't fork"); + + if (pid) { + ssh_free(client->session); + } else { + ssh_bind_free(bind); + tmate_debug("Child spawned pid=%d", getpid()); + client_bootstrap(client); + } + } +} diff --git a/tmate.1 b/tmate.1 new file mode 120000 index 00000000..44d7405b --- /dev/null +++ b/tmate.1 @@ -0,0 +1 @@ +tmux.1 \ No newline at end of file diff --git a/tmate.h b/tmate.h new file mode 100644 index 00000000..bec9ef97 --- /dev/null +++ b/tmate.h @@ -0,0 +1,110 @@ +#ifndef TMATE_H +#define TMATE_H + +#include +#include +#include + +#include "tmux.h" + +#define tmate_debug(str, ...) log_debug("[tmate] " str, ##__VA_ARGS__) +#define tmate_warn(str, ...) log_warn("[tmate] " str, ##__VA_ARGS__) +#define tmate_info(str, ...) log_info("[tmate] " str, ##__VA_ARGS__) +#define tmate_fatal(str, ...) log_fatal("[tmate] " str, ##__VA_ARGS__) + +/* tmate-encoder.c */ + +enum tmate_notifications { + TMATE_CLIENT_KEY, + TMATE_CLIENT_RESIZE, +}; + +struct tmate_encoder { + struct evbuffer *buffer; + struct event ev_readable; + msgpack_packer pk; +}; + +extern void tmate_encoder_init(struct tmate_encoder *encoder); + +extern void tmate_write_header(void); +extern void tmate_write_pane(int pane, const char *data, size_t size); + +extern void tmate_client_key(int key); +extern void tmate_client_resize(u_int sx, u_int sy); + +/* tmate-decoder.c */ + +#define TMATE_HLIMIT 1000 +#define TMATE_MAX_MESSAGE_SIZE (16*1024) + +enum tmate_commands { + TMATE_HEADER, + TMATE_SYNC_WINDOW, + TMATE_PTY_DATA, +}; + +#define TMATE_PANE_ACTIVE 1 + +struct tmate_decoder { + struct msgpack_unpacker unpacker; +}; + +extern void tmate_decoder_init(struct tmate_decoder *decoder); +extern void tmate_decoder_get_buffer(struct tmate_decoder *decoder, + char **buf, size_t *len); +extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len); + +/* tmate-ssh-client.c */ + +typedef struct ssh_session_struct* ssh_session; +typedef struct ssh_channel_struct* ssh_channel; + +struct tmate_ssh_client { + ssh_session session; + ssh_channel channel; + + struct tmate_encoder *encoder; + struct tmate_decoder *decoder; + + struct winsize winsize_pty; + + struct event ev_ssh; +}; +extern void tmate_ssh_client_init(struct tmate_ssh_client *ssh_client, + struct tmate_encoder *encoder, + struct tmate_decoder *decoder); + +/* tmate-ssh-client-pty.c */ + +struct tmate_ssh_client_pty { + ssh_session session; + ssh_channel channel; + + int pty; + struct winsize winsize_pty; + + struct event ev_ssh; + struct event ev_pty; +}; + +extern void tmate_ssh_client_pty_init(struct tmate_ssh_client_pty *client); +extern void tmate_flush_pty(struct tmate_ssh_client_pty *client); + +/* tmate-ssh-server.c */ + +#define SSH_BANNER "tmate" + +extern void tmate_ssh_server_main(int port); + +/* tmate-server.c */ + +extern struct tmate_encoder *tmate_encoder; + +extern void tmate_spawn_slave_server(struct tmate_ssh_client *client); +extern void tmate_spawn_slave_client(struct tmate_ssh_client *client); + +/* tmate-debug.c */ +extern void tmate_print_trace(void); + +#endif diff --git a/tmux.h b/tmux.h index 9c91d6a4..7d73ee22 100644 --- a/tmux.h +++ b/tmux.h @@ -19,6 +19,8 @@ #ifndef TMUX_H #define TMUX_H +#define TMATE_SLAVE + #define PROTOCOL_VERSION 7 #include @@ -950,8 +952,13 @@ struct window_pane { struct event changes_timer; u_int changes_redraw; +#ifdef TMATE_SLAVE +#define PANE_KILL 0x10 + struct evbuffer *event_input; +#else int fd; struct bufferevent *event; +#endif struct input_ctx ictx; @@ -1714,8 +1721,10 @@ int paste_free_index(struct paste_stack *, u_int); void paste_add(struct paste_stack *, char *, size_t, u_int); int paste_replace(struct paste_stack *, u_int, char *, size_t); char *paste_print(struct paste_buffer *, size_t); +#ifndef TMATE_SLAVE void paste_send_pane(struct paste_buffer *, struct window_pane *, const char *, int); +#endif /* clock.c */ extern const char clock_table[14][5][5]; @@ -1967,10 +1976,12 @@ void input_init(struct window_pane *); void input_free(struct window_pane *); void input_parse(struct window_pane *); +#ifndef TMATE_SLAVE /* input-key.c */ void input_key(struct window_pane *, int); void input_mouse(struct window_pane *, struct session *, struct mouse_event *); +#endif /* xterm-keys.c */ char *xterm_keys_lookup(int); diff --git a/tty-keys.c b/tty-keys.c index 3055f399..14a06a4b 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -26,6 +26,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Handle keys input from the outside terminal. tty_default_*_keys[] are a base @@ -301,10 +302,10 @@ tty_keys_add(struct tty *tty, const char *s, int key) keystr = key_string_lookup_key(key); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { - log_debug("new key %s: 0x%x (%s)", s, key, keystr); + log_debug2("new key %s: 0x%x (%s)", s, key, keystr); tty_keys_add1(&tty->key_tree, s, key); } else { - log_debug("replacing key %s: 0x%x (%s)", s, key, keystr); + log_debug2("replacing key %s: 0x%x (%s)", s, key, keystr); tk->key = key; } } @@ -455,7 +456,7 @@ tty_keys_next(struct tty *tty) len = EVBUFFER_LENGTH(tty->event->input); if (len == 0) return (0); - log_debug("keys are %zu (%.*s)", len, (int) len, buf); + log_debug2("keys are %zu (%.*s)", len, (int) len, buf); /* Is this device attributes response? */ switch (tty_keys_device(tty, buf, len, &size)) { @@ -535,7 +536,7 @@ first_key: goto complete_key; partial_key: - log_debug("partial key %.*s", (int) len, buf); + log_debug2("partial key %.*s", (int) len, buf); /* If timer is going, check for expiration. */ if (tty->flags & TTY_TIMER) { @@ -562,7 +563,7 @@ partial_key: return (0); complete_key: - log_debug("complete key %.*s %#x", (int) size, buf, key); + log_debug2("complete key %.*s %#x", (int) size, buf, key); /* Remove data from buffer. */ evbuffer_drain(tty->event->input, size); @@ -673,7 +674,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) else y = value; } - log_debug("mouse input: %.*s", (int) *size, buf); + log_debug2("mouse input: %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (b < 32 || x < 33 || y < 33) @@ -714,7 +715,7 @@ tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) return (-1); y = 10 * y + (c - '0'); } - log_debug("mouse input (sgr): %.*s", (int) *size, buf); + log_debug2("mouse input (sgr): %.*s", (int) *size, buf); /* Check and return the mouse input. */ if (x < 1 || y < 1) @@ -824,7 +825,7 @@ tty_keys_device(struct tty *tty, const char *buf, size_t len, size_t *size) if (*endptr != ';') class = 0; - log_debug("received service class %u", class); + log_debug2("received service class %u", class); tty_set_class(tty, class); return (0); diff --git a/tty.c b/tty.c index ab75d948..9e6a88f1 100644 --- a/tty.c +++ b/tty.c @@ -30,6 +30,7 @@ #include #include "tmux.h" +#include "tmate.h" void tty_read_callback(struct bufferevent *, void *); void tty_error_callback(struct bufferevent *, short, void *); diff --git a/window-copy.c b/window-copy.c index 51a8f108..173550f8 100644 --- a/window-copy.c +++ b/window-copy.c @@ -178,8 +178,10 @@ window_copy_init(struct window_pane *wp) data->searchtype = WINDOW_COPY_OFF; data->searchstr = NULL; +#ifndef TMATE_SLAVE if (wp->fd != -1) bufferevent_disable(wp->event, EV_READ|EV_WRITE); +#endif data->jumptype = WINDOW_COPY_OFF; data->jumpchar = '\0'; @@ -241,8 +243,10 @@ window_copy_free(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; +#ifndef TMATE_SLAVE if (wp->fd != -1) bufferevent_enable(wp->event, EV_READ|EV_WRITE); +#endif free(data->searchstr); free(data->inputstr); diff --git a/window.c b/window.c index 7678adc6..6c0466cf 100644 --- a/window.c +++ b/window.c @@ -674,8 +674,12 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->shell = NULL; wp->cwd = NULL; +#ifdef TMATE_SLAVE + wp->event_input = evbuffer_new(); +#else wp->fd = -1; wp->event = NULL; +#endif wp->mode = NULL; @@ -709,10 +713,14 @@ window_pane_destroy(struct window_pane *wp) if (event_initialized(&wp->changes_timer)) evtimer_del(&wp->changes_timer); +#ifdef TMATE_SLAVE + evbuffer_free(wp->event_input); +#else if (wp->fd != -1) { bufferevent_free(wp->event); close(wp->fd); } +#endif input_free(wp); @@ -737,6 +745,9 @@ int window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, const char *cwd, struct environ *env, struct termios *tio, char **cause) { +#ifdef TMATE_SLAVE + return 0; +#else struct winsize ws; char *argv0, paneid[16]; const char *ptr; @@ -824,6 +835,7 @@ window_pane_spawn(struct window_pane *wp, const char *cmd, const char *shell, bufferevent_enable(wp->event, EV_READ|EV_WRITE); return (0); +#endif } void @@ -869,16 +881,21 @@ window_pane_read_callback(unused struct bufferevent *bufev, void *data) struct window_pane *wp = data; char *new_data; size_t new_size; +#ifdef TMATE_SLAVE + struct evbuffer *evb = wp->event_input; +#else + struct evbuffer *evb = wp->event->input; +#endif - new_size = EVBUFFER_LENGTH(wp->event->input) - wp->pipe_off; + new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off; if (wp->pipe_fd != -1 && new_size > 0) { - new_data = EVBUFFER_DATA(wp->event->input); + new_data = EVBUFFER_DATA(evb); bufferevent_write(wp->pipe_event, new_data, new_size); } input_parse(wp); - wp->pipe_off = EVBUFFER_LENGTH(wp->event->input); + wp->pipe_off = EVBUFFER_LENGTH(evb); /* * If we get here, we're not outputting anymore, so set the silence @@ -1022,6 +1039,7 @@ window_pane_reset_mode(struct window_pane *wp) wp->flags |= PANE_REDRAW; } +#ifndef TMATE_SLAVE void window_pane_key(struct window_pane *wp, struct session *sess, int key) { @@ -1070,14 +1088,17 @@ window_pane_mouse( } else if (wp->fd != -1) input_mouse(wp, sess, m); } +#endif int window_pane_visible(struct window_pane *wp) { struct window *w = wp->window; +#ifndef TMATE_SLAVE if (wp->layout_cell == NULL) return (0); +#endif if (wp->xoff >= w->sx || wp->yoff >= w->sy) return (0); if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)